#install.packages("knitr")
#install.packages("grid")
#install.packages("plotly")
#install.packages("dprep")
#install.packages("normalr")
#install.packages("ggcorrplot")

#install.packages("RColorBrewer")
#install.packages("rgdal")
#install.packages("jsonlite")
#install.packages("readr")
#install.packages("readr")
library(gridExtra)
library(dplyr)
library(lubridate)
library(magrittr)
library(ggplot2)
library(tidyr)
library(knitr)
#library(normalr)
library(ggcorrplot)

library(leaflet)
library(plotly)
library(RColorBrewer)
library(readr)
#library(MLRMPA)
#??src_mysql
my_db <- src_mysql(
  dbname = "coronavirus",
  host = "localhost"
)
my_db
src:  mysql 10.4.17-MariaDB [root@localhost:/coronavirus]
tbls: avg_world_temp_2020, covid19_confirmed, covid19_deaths, covid19_recovered, covid19_thailand,
  covidus, data_gender, data_lockdown, data_population, gdp_us, gdp19, healthranking, population,
  pornhub, sars_2003_update, time_series_covid19_confirmed_global, time_series_covid19_deaths_global,
  time_series_covid19_recovered_global, us_homeless
##import data
df_conf <- tbl(my_db, sql("select * from time_series_covid19_confirmed_global"))
df_conf <- as.data.frame(df_conf)
df_conf
df_deaths <- tbl(my_db, sql("select * from time_series_covid19_deaths_global"))
df_deaths <- as.data.frame(df_deaths)
df_deaths
df_recover <- tbl(my_db, sql("select * from time_series_covid19_recovered_global"))
df_recover <- as.data.frame(df_recover)
df_recover
##check the time frame of the data
n.col <- ncol(df_conf)
dates <- names(df_conf)[5:n.col]%>% mdy()
range(dates)
[1] "2020-01-22" "2021-01-22"
min.date <- min(dates)
max.date <- max(dates)
min.date.txt <- min.date %>% format('%d %b %Y')
max.date.txt <- max.date %>% format('%d %b %Y')
#clean data
cleanData <- function(data) {
  ## remove some columns
  data %<>% select(-c(Province.State, Lat, Long)) %>% rename(country=Country.Region)
  ## convert from wide to long format
  data %<>% gather(key=date, value=count, -country)
  ## convert from character to date
  data %<>% mutate(date = date %>% mdy())
  ## aggregate by country
  data %<>% group_by(country, date) %>% summarise(count=sum(count, na.rm=T)) %>% as.data.frame()
  return(data)
}
## clean the three data sets
data.confirmed <- df_conf %>% cleanData() %>% rename(confirmed=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data.deaths <- df_deaths %>% cleanData() %>% rename(deaths=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data.recovered <- df_recover %>% cleanData() %>% rename(recovered=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data <- data.confirmed %>% merge(data.deaths, all=T) %>% merge(data.recovered, all=T)
data
## countries/regions with confirmed cases, excl. cruise ships
countries <- data %>% pull(country) %>% setdiff('Cruise Ship')
data 
data.world <- data %>% group_by(date) %>%
  summarise(country='World',
            confirmed = sum(confirmed, na.rm=T),
            deaths = sum(deaths, na.rm=T),
            recovered = sum(recovered, na.rm=T))
`summarise()` ungrouping output (override with `.groups` argument)
data %<>% rbind(data.world)
data
data %<>% mutate(current.confirmed = confirmed - deaths - recovered)
data
NA
#rate
data %<>% arrange(country, date)
n <- nrow(data)
day1 <- min(data$date)
data %<>% mutate(new.confirmed = ifelse(date == day1, NA, confirmed - lag(confirmed, n=1)),
                 new.deaths = ifelse(date == day1, NA, deaths - lag(deaths, n=1)),
                 new.recovered = ifelse(date == day1, NA, recovered - lag(recovered, n=1)))
data %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
                 new.deaths = ifelse(new.deaths < 0, 0, new.deaths),
                 new.recovered = ifelse(new.recovered < 0, 0, new.recovered))
## death rate based on total deaths and recovered cases
data %<>% mutate(rate.upper = (100 * deaths / (deaths + recovered)) %>% round(1))
## lower bound: death rate based on total confirmed cases
data %<>% mutate(rate.lower = (100 * deaths / confirmed) %>% round(1))
## death rate based on the number of death/recovered on every single day
data %<>% mutate(rate.daily = (100 * new.deaths / (new.deaths + new.recovered)) %>% round(1))
View(data)
## convert from wide to long format
data.long <- data %>%
  select(c(country, date, confirmed, current.confirmed, recovered, deaths)) %>%
  gather(key=type, value=count, -c(country, date))
## set factor levels to show them in a desirable order
data.long %<>% mutate(type=recode_factor(type, confirmed='Total Confirmed',
                                         current.confirmed='Current Confirmed',
                                         recovered='Recovered',
                                         deaths='Deaths'))
View(data.long)
##Number of case World
world <- filter(data.long,country == 'World')
plot1 <- world %>% filter(type != 'Total Confirmed') %>%
  ggplot(aes(x=date, y=count)) +
  geom_area(aes(fill=type), alpha=0.5) +
  labs(title=paste0('Numbers of Cases Worldwide - ', max.date.txt)) +
  scale_fill_manual(values=c('red', 'green', 'black')) +
  theme(legend.title=element_blank(), legend.position='bottom',
        plot.title = element_text(size=7),
        axis.title.x=element_blank(),
        axis.title.y=element_blank(),
        legend.key.size=unit(0.2, 'cm'),
        legend.text=element_text(size=6),
        axis.text=element_text(size=7),
        axis.text.x=element_text(angle=45, hjust=1))
plot2 <- world %>%
  ggplot(aes(x=date, y=count)) +
  geom_line(aes(color=type)) +
  labs(title=paste0('Numbers of Cases Worldwide (log scale) - ', max.date.txt)) +
  scale_color_manual(values=c('purple', 'red', 'green', 'black')) +
  theme(legend.title=element_blank(), legend.position='bottom',
        plot.title = element_text(size=14),
        axis.title.x=element_blank(),
        axis.title.y=element_blank(),
        legend.key.size=unit(0.2, 'cm'),
        legend.text=element_text(size=14),
        axis.text=element_text(size=14),
        axis.text.x=element_text(angle=45, hjust=1)) +
  scale_y_continuous(trans='log10')
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)

## Current Confirmed Cases
data.world <- data %>% filter(country=='World')
n <- nrow(data.world)
plot1 <- ggplot(data.world, aes(x=date, y=current.confirmed)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='Current Confirmed Cases') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=new.confirmed)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='Daily New Confirmed Cases') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)

View(data.world)
## a scatter plot with a smoothed line and vertical x-axis labels
plot1 <- ggplot(data.world, aes(x=date, y=deaths)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='Accumulative Deaths') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=recovered)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='Accumulative Recovered Cases') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot3 <- ggplot(data.world, aes(x=date, y=new.deaths)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='New Deaths') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot4 <- ggplot(data.world, aes(x=date, y=new.recovered)) +
  geom_point() + geom_smooth() +
  xlab('') + ylab('Count') + labs(title='New Recovered Cases') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
## show four plots together, with 2 plots in each row
grid.arrange(plot1, plot2, plot3, plot4, nrow=2)

## convert from wide to long format, for drawing area plots
rates.long <- data %>%
  select(c(country, date, rate.upper, rate.lower, rate.daily)) %>%
  gather(key=type, value=count, -c(country, date))
# set factor levels to show them in a desirable order
rates.long %<>% mutate(type=recode_factor(type, rate.daily='Daily',
                             rate.upper='Upper bound'))
## ranking by confirmed cases
data.latest.all <- data %>% filter(date == max(date)) %>%
  select(country, date,confirmed, new.confirmed, current.confirmed,
         recovered, deaths, new.deaths, death.rate=rate.lower) %>%
  mutate(ranking = dense_rank(desc(confirmed)))
#View(data.latest.all)
k <- 20
## top 20 countries: 21 incl. 'World'
top.countries <- data.latest.all %>% filter(ranking <= k + 1) %>%
  arrange(ranking) %>% pull(country) %>% as.character()
top.countries %>% setdiff('World') %>% print()
 [1] "US"             "India"          "Brazil"         "Russia"         "United Kingdom"
 [6] "France"         "Spain"          "Italy"          "Turkey"         "Germany"       
[11] "Colombia"       "Argentina"      "Mexico"         "Poland"         "South Africa"  
[16] "Iran"           "Ukraine"        "Peru"           "Indonesia"      "Netherlands"   
data.latest <- data.latest.all %>% filter(!is.na(country)) %>%
  mutate(country=ifelse(ranking <= k + 1, as.character(country), 'Others')) %>%
  mutate(country=country %>% factor(levels=c(top.countries, 'Others')))
data.latest %<>% group_by(country) %>%
  summarise(confirmed=sum(confirmed), new.confirmed=sum(new.confirmed),
            current.confirmed=sum(current.confirmed),
            recovered=sum(recovered), deaths=sum(deaths), new.deaths=sum(new.deaths)) %>%
  mutate(death.rate=(100 * deaths/confirmed) %>% round(1)) 
`summarise()` ungrouping output (override with `.groups` argument)
data.latest
data.latest %<>% select(c(country, confirmed, deaths, death.rate,
                          new.confirmed, new.deaths, current.confirmed,recovered)) %>%
  mutate(recover.rate=(100 * recovered/confirmed) %>% round(1))
data.latest
df_pop <- tbl(my_db, sql("select * from population "))
df_pop <- as.data.frame(df_pop)
df_pop <- rename(df_pop,"country"="Country")
df_pop
data.latest <- merge(x = data.latest, y = df_pop, by = "country", all.x = TRUE) 
data.latest <- rename(data.latest,"population" = "Population (2020)")
data.latest
data.latest  %<>% select(c(country, confirmed, deaths, death.rate,
                          new.confirmed, new.deaths,
                          current.confirmed,recovered,recover.rate,population)) %>%
  mutate(confirm.rate=(100 *confirmed/population) %>% round(1))
data.latest
NA
data.latest %>% mutate(death.rate=death.rate %>% format(nsmall=1) %>% paste0('%'))
NA
NA
## convert from wide to long format, for drawing area plots
data.latest.long <- data.latest %>% filter(country!='World') %>%
  gather(key=type, value=count, -country)
## set factor levels to show them with proper text and in a desirable order
data.latest.long %<>% mutate(type=recode_factor(type,
                                                confirmed='Total Confirmed',
                                                deaths='Total Deaths',
                                                death.rate='Death Rate (%)',
                                                new.confirmed='New Confirmed (compared with one day before)',
                                                new.deaths='New Deaths (compared with one day before)',
                                                current.confirmed='Current Confirmed',
                                                recover.rate = 'Recover Rate(%)',
                                                confirm.rate = 'Confirmed Rate(%)'))
#View(data.latest.long)
data.one.dem <- filter(data.latest.long,type=='Total Confirmed'
                       | type=='Total Deaths'
                       | type=='Current Confirmed')
data.two.dem <- filter(data.latest.long,type=='Confirmed Rate(%)'
                       #| type=='New Confirmed (compared with one day before)'
                       #| type=='New Deaths (compared with one day before)'
                       | type=='Death Rate (%)'
                       | type=='Recover Rate(%)')
data.two.dem
## bar chart
data.one.dem %>% ggplot(aes(x=country, y=count, fill=country, group=country)) +
  geom_bar(stat='identity') +
  geom_text(aes(label=count, y=count), size=2, vjust=0) +
  xlab('') + ylab('') +
  labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
  scale_fill_discrete(name='Country', labels=aes(count)) +
  theme(legend.title=element_blank(),
        legend.position='none',
        plot.title=element_text(size=11),
        axis.text=element_text(size=7),
        axis.text.x=element_text(angle=45, hjust=1)) +
  facet_wrap(~type, ncol=1, scales='free_y')

data.two.dem$facet <- factor(data.two.dem$type, levels = c('Confirmed Rate(%)', 'Recover Rate(%)','Death Rate (%)'))
data.two.dem %>% 
  ggplot(aes(x=country, y=count, fill=country, group=country)) +
  geom_bar(stat='identity') +
  geom_text(aes(label=count, y=count), size=2, vjust=0) +
  xlab('') + ylab('') +
  labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
  scale_fill_discrete(name='Country', labels=aes(count)) +
  theme(legend.title=element_blank(),
        legend.position='none',
        plot.title=element_text(size=11),
        axis.text=element_text(size=9),
        axis.text.x=element_text(size=6,angle=45, hjust=1)) +
  facet_wrap(~facet, ncol=1, scales='free_y')

#df_gdp
df_gdp2019 <- tbl(my_db, sql("select * from gdp19"))
df_gdp2019 <- as.data.frame(df_gdp2019)
df_gdp2019
NA
#healthranking
df_healt <- tbl(my_db, sql("select * from healthranking"))
df_healt <- as.data.frame(df_healt)
df_healt <- select(df_healt,c("country","healthCareIndex"))
df_healt
#Top20Pornhub
df_pornhub <- tbl(my_db, sql("select * from Pornhub"))
df_pornhub <- as.data.frame(df_pornhub)
df_pornhub
NA
#temp
df_temp <- tbl(my_db, sql("select * from Avg_World_Temp_2020"))
df_temp <- as.data.frame(df_temp)
df_city <- select(df_temp,c("Country","City")) %>%
  rename(country=Country) %>% 
  rename(city=City)
numofcity <- aggregate(city ~ country, data = df_city, length)
df_temp <- select(df_temp,c("Country","Apr","May","Jun","Jul","Aug")) %>%
  rename(country=Country)
df_temp <- data.frame(country=df_temp[,1],avg=rowMeans(df_temp[,-1]))
df_temp <- df_temp %<>% group_by(country) %>% summarise(avg_temp = mean(avg,na.rm = TRUE))
`summarise()` ungrouping output (override with `.groups` argument)
df_temp <- df_temp %>% mutate(country=ifelse(country=="United States","US", country ) ) 
df_temp$avg_temp <- df_temp$avg_temp %>% 
  sprintf(df_temp$avg_temp, fmt = '%#.1f') %>%
  as.numeric(df_temp$avg_temp)
df_temp
#Top 20 with gdp
data.longGDP <- df_gdp %>% gather(key=year, value=GDP, -c(country))
data.top <- data.latest %>% filter(country!='World')
data.top <- head(data.top,20)
View(data.latest)
#merge
mergcountry = function(data1,data2){
  data <- merge(x = data1, y = data2, by = "country", all.x = TRUE) 
  return(data)
}
data.top.world <- merge(x = data.top, y = df_gdp2019, by = "country", all.x = TRUE) %>% 
  select(-c(code,rank,new.confirmed,new.deaths,current.confirmed,population)) %>% 
  rename(GDP="GDP (millions of US dollars)")

data.top.world <- merge(x = data.top.world, y = df_healt, by = "country", all.x = TRUE) %>%
  rename(healthcare="healthCareIndex")
#data.top.world <- mergcountry(data.top.world, df_temp)

data.top.world <- merge(x = data.top.world, y = df_pornhub, by = "country", all.x = TRUE) %>%
  rename(Pornhub = "PornhubIndex(%)")

data.top.world <- mergcountry(data.top.world, df_temp)
index <- is.na(data.top.world)
data.top.world[index] <- 0
data.top.world
View(data.top.world)

normalize = function(data){
  #return ((data - min(data,na.rm = TRUE))/(max(data,na.rm = TRUE) - min(data,na.rm = TRUE)))
  z <- scale(data);
  tanh(z/2)
}
norm_data = as.data.frame(apply(data.top.world[,2:12],2,normalize))
corr_data <- norm_data
norm_data$country <- c("Argentina","Bangladesh","Brazil","Chile","Colombia","France","Germany","India","Iran","Italy","Mexico","Pakistan","Peru","Russia","saudi Arabia","South Africa","Spain","Turkey","United Kingdom","US")
#View(norm_data)


norm_data_plot <- select(norm_data,"country","confirm.rate","death.rate","recover.rate","healthcare","Pornhub","GDP","avg_temp")
norm_data_plot %<>% gather(key=type, value=count, -c(country))
level_order <- factor(norm_data_plot$type, 
                      level = c("GDP","avg_temp","healthcare","recover.rate","death.rate","confirm.rate","Pornhub"))
ggplot(data = norm_data_plot, aes(x=country, y=level_order, fill=count)) + 
  geom_tile() +
  scale_fill_gradient(low = "pink", high = "blue") +
  xlab("") +
  ylab("") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90,vjust = 1))+
  theme(
    axis.line = element_blank(),
    axis.ticks = element_blank(),
    panel.grid.minor = element_blank(),
    panel.grid.major = element_blank(),
    panel.border = element_blank(),
    panel.background = element_blank(),
    #legend.position = "none"
  )

NA
NA
#correlation
corr_data %<>% select(c(GDP,confirm.rate,death.rate,recover.rate,healthcare,avg_temp,Pornhub))
head(corr_data)
cor(corr_data)
                    GDP confirm.rate  death.rate recover.rate  healthcare    avg_temp     Pornhub
GDP           1.0000000    0.4342894 -0.23113988   -0.3937431  0.25183938  0.15986445  0.69972226
confirm.rate  0.4342894    1.0000000 -0.34422360   -0.7182585  0.48153104 -0.36761397  0.44060606
death.rate   -0.2311399   -0.3442236  1.00000000    0.1733012 -0.17258588  0.26319481 -0.06882314
recover.rate -0.3937431   -0.7182585  0.17330123    1.0000000 -0.76240251  0.12250932 -0.40581644
healthcare    0.2518394    0.4815310 -0.17258588   -0.7624025  1.00000000 -0.05087304  0.35166155
avg_temp      0.1598645   -0.3676140  0.26319481    0.1225093 -0.05087304  1.00000000  0.10674647
Pornhub       0.6997223    0.4406061 -0.06882314   -0.4058164  0.35166155  0.10674647  1.00000000
ggcorrplot(cor(corr_data),hc.order = TRUE,
           outline.color = "white",
           colors = c("#6D9EC1","white","#E46726"),
           lab = TRUE)

df <- data.long %>% filter(country %in% top.countries) %<>%
  mutate(country=country %>% factor(levels=c(top.countries)))
Error in data.long %>% filter(country %in% top.countries) <- list(country = c(13L,  : 
  could not find function "%>%<-"
p <- df %>% filter(country != 'World') %>%
  ggplot(aes(x=date, y=count, color=type)) +
  geom_line() +
  labs(title=paste0('Numbers of COVID-19 Cases in Top 20 Countries (log scale) - ',
                    max.date.txt)) +
  scale_color_manual(values=c('purple', 'red', 'green', 'black')) +
  theme(legend.title=element_blank(), legend.position='bottom',
        plot.title = element_text(size=10),
        axis.title.x=element_blank(),
        axis.title.y=element_blank(),
        legend.key.size=unit(0.4, 'cm'),
        legend.text=element_text(size=10),
        strip.text.x=element_text(size=10),
        axis.text=element_text(size=10),
        axis.text.x=element_text(angle=45, hjust=1)) +
  scale_y_continuous(trans='log10')
p + facet_wrap(~country, ncol=4, scales='free_y')
data.world %<>% arrange(desc(date)) %>%
  select(c(date, confirmed, deaths, recovered, current.confirmed,new.confirmed, new.deaths, new.recovered, rate.lower, rate.upper, rate.daily))
data.world %>%
  mutate(rate.upper = rate.upper %>% format(nsmall=1) %>% paste0('\\%'),
         rate.lower = rate.lower %>% format(nsmall=1) %>% paste0('\\%'),
         rate.daily = rate.daily %>% format(nsmall=1) %>% paste0('\\%')) 
#sars_2003
df_sars <- tbl(my_db, sql("select * from sars_2003_update"))
df_sars <- as.data.frame(df_sars)
df_sars
## convert from character to date
#datesSar <- as.Date(df_sars$Date,format = "%m/%d/%y")

df_sars %<>%  mutate(Date = as.Date(df_sars$Date,format = "%m/%d/%y"))
df_sars
## convert from wide to long format
dataSar.long <- df_sars %>%
  select(c(Date, country, Cumulative_number , Number_deaths, Number_recovered)) %>%
  gather(key=type, value=count, -c(country, Date))
## set factor levels to show them in a desirable order
dataSar.long %<>% mutate(type=recode_factor(type, Cumulative_number ='Cumulative Number',
                                         Number_deaths ='Number of deaths',
                                         Number_recovered ='Number of recovered'))
View(dataSar.long)
 
#Covid_Thailand
df_thai <- tbl(my_db, sql("select * from covid19_Thailand"))
df_thai <- as.data.frame(df_thai)
View(df_thai)
#clean Covid_Thailand
dates.th <- df_thai[,2]%>% mdy()
range(dates.th)
[1] "2020-01-12" "2021-01-15"
min.date.th <- min(dates.th)
max.date.th <- max(dates.th)
min.date.txt.th <- min.date.th %>% format('%d %b %Y')
max.date.txt.th <- max.date.th %>% format('%d %b %Y')
df_thai$announce_date <- mdy(df_thai$announce_date)
df_thai$notification_date <- mdy(df_thai$notification_date)
df_thai 
df_thai <- df_thai %>% select(!No.) %>% select(!notification_date) %>% 
  group_by(announce_date)
Error: Can't subset columns that don't exist.
x Column `No.` doesn't exist.
# Total confirmed cases in Thailand
data.thai.count <- df_thai %>%
  select(announce_date) %>%
  summarise(comfirmed = n())  %>% as.data.frame()
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.count$cumulative_confirmed <- cumsum(data.thai.count[, 2])
data.thai.count
## Thai Confirmed Cases (Jan 2020 - Jan 2021
plot1 <- ggplot(data.thai.count, aes(x=announce_date, y=cumulative_confirmed)) +
  geom_point() + geom_smooth() +
  xlab(" ") + ylab("Count") + labs(title='Thai Cumulative Confirmed Cases (Jan 2020 - Jan 2021)') +
  theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.thai.count, aes(x=announce_date, y=comfirmed)) +
  geom_point() + geom_smooth() +
    xlab(" ") + ylab("Count")+ labs(title='Thai Confirmed Cases (Jan 2020 - Jan 2021 log scale)') +
   theme(axis.text.x=element_text(angle=45, hjust=1))+scale_y_continuous(trans='log10')
## show two plots side by side
grid.arrange(plot1, plot2, ncol=1)

## Thai Confirmed Cases (Jan 2020 - Jan 2021) log scale
plot1 <- ggplot(data.thai.count, aes(x=announce_date, y=cumulative_confirmed)) +
  geom_point() + geom_smooth() +
  xlab(" ") + ylab("Count") + labs(title='Thai Cumulative Confirmed Cases (Jan 2020 - Jan 2021 log scale)') +
  theme(axis.text.x=element_text(angle=45, hjust=1))+scale_y_continuous(trans='log10')
plot2 <- ggplot(data.thai.count, aes(x=announce_date, y=comfirmed)) +
  geom_point() + geom_smooth() +
  xlab(" ") + ylab("Count")+ labs(title='Thai Confirmed Cases (Jan 2020 - Jan 2021 log scale)') +
  theme(axis.text.x=element_text(angle=45, hjust=1))+scale_y_continuous(trans='log10')
## show two plots side by side
grid.arrange(plot1, plot2, ncol=1)

View(df_thai)
# Confirmed cases divided by sex (gender)
df_thai$sex[df_thai$sex == ""] <- "Unknown"
data.thai.gender <- df_thai %>%
  group_by(sex) %>%
  summarise(count = n()) %>%
  mutate(percent = (count / sum(count) * 100) %>% round(2)) %>%
  filter(percent>1)%>%
  #mutate(pos = cumsum(percent) - 0.5*percent) %>%
  arrange(desc(percent))
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.gender
data.thai.gender$sex <- factor(data.thai.gender$sex, levels = as.character(data.thai.gender$sex))
data.thai.gender$sex
[1] Female  Male    Unknown
Levels: Female Male Unknown
g.th.gender <- data.thai.gender %>% 
  ggplot(aes(x = "", y = percent, fill = sex)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar("y") +
  theme_void() +
  labs(title='Gender of Thai Confirmed Cases (Jan 2020 - Jan 2021)')+
  geom_text(aes(label = paste0(percent, "%")), color = "white", size = 5, position = position_stack(vjust = 0.5)) +
  guides(fill = guide_legend(reverse = TRUE)) 
g.th.gender

# Confirmed cases divided by province_of_onset
data.thai.onset <- df_thai %>%
    group_by(province_of_onset) %>%
    summarise(count = n()) %>%
    arrange(desc(count))%>%
    rename(onset="count")%>%
    rename(province="province_of_onset")
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.onset$province[data.thai.onset$province == ""] <- "Unknown"
data.thai.onset
# Confirmed cases divided by province_of_onset
data.thai.isolation <- df_thai %>%
    group_by(province_of_isolation) %>%
    summarise(count = n()) %>%
    arrange(desc(count))%>%
    rename(isolation ="count")%>%
    rename(province="province_of_isolation")
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.isolation$province[data.thai.isolation$province == ""] <- "Unknown"
data.thai.isolation
data.province <- merge(x = data.thai.onset, y = data.thai.isolation, by = "province", all.x = TRUE) 
data.province <- data.province%>%filter(onset>125)
data.province
data.province.long <- data.province %>%
    gather(key=type, value=count, -c(province))
data.province.long
ggplot(data.province.long,aes(x=province,y=count))+
    geom_bar(stat = "identity",position = "dodge",aes(fill=type))+
    labs(title = "Province of onset and Province of isolation in Thailand")+
    theme(legend.title=element_blank(),
                  #legend.position='none',
                  plot.title=element_text(size=12),
                  axis.text=element_text(size=10),
                  axis.text.x=element_text(angle=45, hjust=1))+ xlab('') + ylab('Count')

# Confirmed cases divided by risk
data.thai.risk <- df_thai %>%
  group_by(risk) %>%
  summarise(count = n()) %>%
  arrange(desc(count))
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.risk
# Confirmed cases divided by risk
data.thai.risk <- df_thai %>%
  group_by(risk) %>%
  summarise(count = n()) %>%
  mutate(percent = (count / sum(count) * 100) %>% round(2)) %>%
  filter(percent>0.76) %>%
  arrange(desc(percent)) 
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.risk
data.thai.risk$risk[data.thai.risk$risk == "C"] <- "Close contact with the patient"
data.thai.risk$risk[data.thai.risk$risk == "CGustDr Samut SaGGAF"] <- "Cluster Samut Sakhon"
data.thai.risk$risk[data.thai.risk$risk == "F"] <- "State Quarantine"
data.thai.risk$risk[data.thai.risk$risk == "G"] <- "Go to a crowded place"
data.thai.risk$risk[data.thai.risk$risk == "A"] <- "People travelling from abroad"
data.thai.risk$risk[data.thai.risk$risk == "UFGFAwF"] <- "Unknown"
data.thai.risk$risk[data.thai.risk$risk == "D"] <- "Career at risk"
data.thai.risk$risk[data.thai.risk$risk == "CGustDr RayAFg"] <- "Cluster Rayong"
data.thai.risk$risk[data.thai.risk$risk == "CGustDr Pattaya CasCFA"] <- "Cluster Pattaya Casino"
data.thai.risk$risk[data.thai.risk$risk == "CGustDr AFg TGAFg"] <- "Cluster Ang Thong"
data.thai.risk
data.thai.risk$risk <- factor(data.thai.risk$risk, levels = as.character(data.thai.risk$risk))
data.thai.risk$risk
[1] Cluster Samut Sakhon           Close contact with the patient
[3] Go to a crowded place          State Quarantine              
[5] People travelling from abroad  Unknown                       
[7] Cluster Rayong                 Career at risk                
[9] Cluster Pattaya Casino        
9 Levels: Cluster Samut Sakhon ... Cluster Pattaya Casino
g.th.risk <- data.thai.risk %>% 
  ggplot(aes(x = "", y = percent, fill = risk)) +
  geom_bar(stat = "identity", width = 0.5) +
  coord_polar("y") +
  theme_void() +
  labs(title='Risk of Thai Confirmed Cases(Jan 2020 - Jan 2021)')+
  geom_text(aes(label = paste0(percent, "%")), color = "Black", size = 3, position = position_stack(vjust = 0.5)) +
  guides(fill = guide_legend(reverse = TRUE)) 
g.th.risk

# Confirmed cases divided by age
data.thai.age <- df_thai %>%
  group_by(age,sex) %>% 
  filter(age != 0)%>%
  summarise(count = n()) %>%
  arrange(desc(count))
`summarise()` regrouping output by 'age' (override with `.groups` argument)
data.thai.age
ggplot(data.thai.age,aes(x=age,y=count,fill=sex))+geom_bar(stat = "identity")+
      labs(title='Age of Thai Confirmed Cases (Stack)')+guides(fill=guide_legend(reverse = T))

# Confirmed cases divided by nationality


data.thai.nationality <- df_thai %>%
  group_by(nationality) %>%
  summarise(count = n()) %>%
  filter(count > 11)%>%
  arrange(desc(count))
`summarise()` ungrouping output (override with `.groups` argument)
data.thai.nationality$nationality[data.thai.nationality$nationality == "????????"] <- "Unknown"
data.thai.nationality$nationality[data.thai.nationality$nationality == ""] <- "Unknown"
data.thai.nationality$nationality[data.thai.nationality$nationality == "Burma"] <- "Myanmar"
data.thai.nationality
ggplot(data.thai.nationality)+ 
  geom_bar(aes(x=nationality,y=count,fill = count),stat = "identity",show.legend = F)+
    ggtitle('Nationality of Thai Confirmed Cases (log scale)')+
    theme(plot.title = element_text(hjust = 0.5))+
    theme(axis.text = element_text(size = 10),
            axis.title = element_text(size = 10),
            plot.title = element_text(size = 10))+
    xlab("")+ylab("Number of Nationality")+
   scale_y_continuous(trans='log10')+coord_flip()+scale_fill_gradient(low="blue",high = "red")

#Covid_US
df_us <- tbl(my_db, sql("select * from covidUs"))
df_us <- as.data.frame(df_us)
df_us
#clean Covid_US
dates.us <- df_us[,2]%>% mdy()
range(dates.us)
[1] "2020-01-21" "2020-12-24"
min.date.us <- min(dates.us)
max.date.us <- max(dates.us)
min.date.txt.us <- min.date.us %>% format('%d %b %Y')
max.date.txt.us <- max.date.us %>% format('%d %b %Y')
df_us$date <- mdy(df_us$date)
df_us
df_us <- df_us %>% select(!MyUnknownColumn) %>% select(!fips) %>% 
  group_by(date)
df_us
data.us.total <- df_us %>% group_by(date) %>%
  summarise(state ='US',
            cases = sum(deaths,na.rm=T),
            deaths = sum(deaths,na.rm=T))
`summarise()` ungrouping output (override with `.groups` argument)
View(data.us.total)
data.us.latest <- df_us %>%filter(date==max.date.us)
View(data.us.latest)
data.us.latest %<>% mutate(ranking = dense_rank(desc(cases))) %>%arrange(ranking) 
top.us <- data.us.latest[,2]
top.us
# List of top 20 state
k <- 19
data.us.top <- data.us.latest %>%
    filter(ranking <= k+1) %>% 
    arrange(ranking) 
View(data.us.top)
us.state.top <- data.us.top %>% pull(state) %>% as.character()
us.state.top  %>% setdiff('US') %>% print()
 [1] "California"     "Texas"          "Florida"        "Illinois"       "New York"      
 [6] "Ohio"           "Georgia"        "Pennsylvania"   "Tennessee"      "Michigan"      
[11] "Wisconsin"      "North Carolina" "Indiana"        "Arizona"        "New Jersey"    
[16] "Minnesota"      "Missouri"       "Massachusetts"  "Alabama"        "Virginia"      
View(us.state.top)
# confirmed rate & death rate of top 20 state
top_us <- data.us.latest %>% filter(state %in% us.state.top & state != "US")
View(top_us)
#gender in us
df_gender_us <- tbl(my_db, sql("select * from data_gender"))
df_gender_us <- as.data.frame(df_gender_us)
df_gender_us <- select(df_gender_us,c("State","Male","Female"))
df_gender_us <- rename(df_gender_us,"state"="State")
df_gender_us
#population in us
df_pop_us <- tbl(my_db, sql("select * from data_population"))
df_pop_us <- as.data.frame(df_pop_us)
df_pop_us <- select(df_pop_us,c("State","Population"))
df_pop_us <- rename(df_pop_us,"state"="State")
df_pop_us
#lockdown in us
df_lockdown_us <- tbl(my_db, sql("select * from data_lockdown"))
df_lockdown_us <- as.data.frame(df_lockdown_us)
df_lockdown_us <- select(df_lockdown_us,c("State","Day lockdown"))
df_lockdown_us <- rename(df_lockdown_us,"state"="State")
df_lockdown_us
#GDP in us
df_gdp_us <- tbl(my_db, sql("select * from gdp_us"))
df_gdp_us <- as.data.frame(df_gdp_us)
df_gdp_us <- select(df_gdp_us,c("State","GDPs"))
df_gdp_us <- rename(df_gdp_us,"state"="State")
df_gdp_us
#homeless in us
df_homeless_us <- tbl(my_db, sql("select * from us_homeless"))
df_homeless_us <- as.data.frame(df_homeless_us)
df_homeless_us <- select(df_homeless_us,c("State","Homeless"))
df_homeless_us <- rename(df_homeless_us,"state"="State")
df_homeless_us
#View(df_us)
#merge
mergcountry = function(data1,data2){
  data <- merge(x = data1, y = data2, by = "state", all.x = TRUE) 
  return(data)
}

df_Allus <- mergcountry(top_us, df_gender_us)
df_Allus <- mergcountry(df_Allus, df_pop_us)
df_Allus <- mergcountry(df_Allus, df_lockdown_us)
df_Allus <- mergcountry(df_Allus, df_gdp_us)
df_Allus <- mergcountry(df_Allus, df_homeless_us)
#df_Allus <- mergcountry(top_us, df_Allus)
View(df_Allus)
index <- is.na(df_Allus)
df_Allus[index] <- 0

normalize = function(data){
  #return ((data - min(data,na.rm = TRUE))/(max(data,na.rm = TRUE) - min(data,na.rm = TRUE)))
  z <- scale(data);
  tanh(z/2)
}
Allus = as.data.frame(apply(df_Allus[,3:11],2,normalize))
corr_dataUS <- Allus 

View(Allus)
Allus$state <- c(df_Allus$state)
Allus <- rename(Allus,"confirmed"="cases")
View(Allus)


Allus_plot <- select(Allus,"state","confirmed","deaths","Male","Female","Population","Day lockdown","GDPs","Homeless")
Allus_plot %<>% gather(key=type, value=count, -c(state))
level_order <- factor(Allus_plot$type, 
                      level = c("confirmed","deaths","Male","Female","Population","Day lockdown","GDPs","Homeless"))
ggplot(data = Allus_plot, aes(x=state, y=level_order, fill=count)) + 
  geom_tile() +
  scale_fill_gradient(low = "pink", high = "blue") +
  xlab("") +
  ylab("") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 90,vjust = 1))+
  theme(
    axis.line = element_blank(),
    axis.ticks = element_blank(),
    panel.grid.minor = element_blank(),
    panel.grid.major = element_blank(),
    panel.border = element_blank(),
    panel.background = element_blank(),
    #legend.position = "none"
  ) +labs(title='The heatmap of COVID-19 infections in US')

NA
NA
NA
corr_dataUS <- rename(corr_dataUS,"Daylockdown"="Day lockdown")
#correlation
corr_dataUS %<>% select(c(cases,deaths,Male,Female,Population,Daylockdown,GDPs,Homeless))
head(corr_dataUS)
cor(corr_dataUS)
                   cases       deaths         Male       Female   Population Daylockdown        GDPs
cases        1.000000000  0.792654639  0.105367061 -0.005945092  0.959941301  -0.2058259  0.91047827
deaths       0.792654639  1.000000000  0.008041947  0.017335533  0.852691131  -0.3797468  0.88497061
Male         0.105367061  0.008041947  1.000000000 -0.877329594 -0.009961687  -0.2898630 -0.02437981
Female      -0.005945092  0.017335533 -0.877329594  1.000000000  0.069101784   0.3629328  0.02235864
Population   0.959941301  0.852691131 -0.009961687  0.069101784  1.000000000  -0.2155126  0.97052821
Daylockdown -0.205825865 -0.379746786 -0.289863040  0.362932815 -0.215512639   1.0000000 -0.36113782
GDPs         0.910478267  0.884970609 -0.024379811  0.022358644  0.970528214  -0.3611378  1.00000000
Homeless     0.791615898  0.796426563 -0.080155515  0.151488777  0.863178014  -0.3573560  0.89961070
               Homeless
cases        0.79161590
deaths       0.79642656
Male        -0.08015552
Female       0.15148878
Population   0.86317801
Daylockdown -0.35735597
GDPs         0.89961070
Homeless     1.00000000
ggcorrplot(cor(corr_dataUS),hc.order = TRUE,
           outline.color = "white",
           colors = c("#6D9EC1","white","#E46726"),
           lab = TRUE)

#View(df_us)
#merge
mergcountry = function(data1,data2){
  data <- merge(x = data1, y = data2, by = "state", all.x = TRUE) 
  return(data)
}

df.Allus <- mergcountry(df_us, df_gender_us)
df.Allus <- mergcountry(df.Allus, df_pop_us)
df.Allus <- mergcountry(df.Allus, df_lockdown_us)
df.Allus <- mergcountry(df.Allus, df_gdp_us)
df.Allus <- mergcountry(df.Allus, df_homeless_us)
#df_Allus <- mergcountry(top_us, df_Allus)
View(df_Allus)
index <- is.na(df.Allus)
df.Allus[index] <- 0

normalize = function(data){
  #return ((data - min(data,na.rm = TRUE))/(max(data,na.rm = TRUE) - min(data,na.rm = TRUE)))
  z <- scale(data);
  tanh(z/2)
}
Allus = as.data.frame(apply(df.Allus[,3:10],2,normalize))
corr_dataUS <- Allus 

View(Allus)
corr_dataUS <- rename(corr_dataUS,"Daylockdown"="Day lockdown")
#correlation
corr_dataUS %<>% select(c(cases,deaths,Male,Female,Population,Daylockdown,GDPs,Homeless))
head(corr_dataUS)
cor(corr_dataUS)
                cases    deaths      Male    Female Population Daylockdown      GDPs  Homeless
cases       1.0000000 0.8771162 0.1273080 0.1904992  0.6171785   0.2038334 0.6075674 0.5556301
deaths      0.8771162 1.0000000 0.1147696 0.1847547  0.6601563   0.2104893 0.6777292 0.6083036
Male        0.1273080 0.1147696 1.0000000 0.9513769  0.1956974   0.5064869 0.1828444 0.1390017
Female      0.1904992 0.1847547 0.9513769 1.0000000  0.2679789   0.5403218 0.2419723 0.1905770
Population  0.6171785 0.6601563 0.1956974 0.2679789  1.0000000   0.3394907 0.9855365 0.8879380
Daylockdown 0.2038334 0.2104893 0.5064869 0.5403218  0.3394907   1.0000000 0.3062257 0.2385315
GDPs        0.6075674 0.6777292 0.1828444 0.2419723  0.9855365   0.3062257 1.0000000 0.9114065
Homeless    0.5556301 0.6083036 0.1390017 0.1905770  0.8879380   0.2385315 0.9114065 1.0000000
ggcorrplot(cor(corr_dataUS),hc.order = TRUE,
           outline.color = "white",
           colors = c("#6D9EC1","white","#E46726"),
           lab = TRUE)

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KI2luc3RhbGwucGFja2FnZXMoImdyaWQiKQ0KI2luc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQojaW5zdGFsbC5wYWNrYWdlcygiZHByZXAiKQ0KI2luc3RhbGwucGFja2FnZXMoIm5vcm1hbHIiKQ0KI2luc3RhbGwucGFja2FnZXMoImdnY29ycnBsb3QiKQ0KDQojaW5zdGFsbC5wYWNrYWdlcygiUkNvbG9yQnJld2VyIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJyZ2RhbCIpDQojaW5zdGFsbC5wYWNrYWdlcygianNvbmxpdGUiKQ0KI2luc3RhbGwucGFja2FnZXMoInJlYWRyIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJyZWFkciIpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShrbml0cikNCiNsaWJyYXJ5KG5vcm1hbHIpDQpsaWJyYXJ5KGdnY29ycnBsb3QpDQoNCmxpYnJhcnkobGVhZmxldCkNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpsaWJyYXJ5KHJlYWRyKQ0KI2xpYnJhcnkoTUxSTVBBKQ0KIz8/c3JjX215c3FsDQpteV9kYiA8LSBzcmNfbXlzcWwoDQogIGRibmFtZSA9ICJjb3JvbmF2aXJ1cyIsDQogIGhvc3QgPSAibG9jYWxob3N0Ig0KKQ0KbXlfZGINCg0KIyNpbXBvcnQgZGF0YQ0KZGZfY29uZiA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSB0aW1lX3Nlcmllc19jb3ZpZDE5X2NvbmZpcm1lZF9nbG9iYWwiKSkNCmRmX2NvbmYgPC0gYXMuZGF0YS5mcmFtZShkZl9jb25mKQ0KZGZfY29uZg0KZGZfZGVhdGhzIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIHRpbWVfc2VyaWVzX2NvdmlkMTlfZGVhdGhzX2dsb2JhbCIpKQ0KZGZfZGVhdGhzIDwtIGFzLmRhdGEuZnJhbWUoZGZfZGVhdGhzKQ0KZGZfZGVhdGhzDQpkZl9yZWNvdmVyIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIHRpbWVfc2VyaWVzX2NvdmlkMTlfcmVjb3ZlcmVkX2dsb2JhbCIpKQ0KZGZfcmVjb3ZlciA8LSBhcy5kYXRhLmZyYW1lKGRmX3JlY292ZXIpDQpkZl9yZWNvdmVyDQpgYGANCmBgYHtyfQ0KIyNjaGVjayB0aGUgdGltZSBmcmFtZSBvZiB0aGUgZGF0YQ0Kbi5jb2wgPC0gbmNvbChkZl9jb25mKQ0KZGF0ZXMgPC0gbmFtZXMoZGZfY29uZilbNTpuLmNvbF0lPiUgbWR5KCkNCnJhbmdlKGRhdGVzKQ0KbWluLmRhdGUgPC0gbWluKGRhdGVzKQ0KbWF4LmRhdGUgPC0gbWF4KGRhdGVzKQ0KbWluLmRhdGUudHh0IDwtIG1pbi5kYXRlICU+JSBmb3JtYXQoJyVkICViICVZJykNCm1heC5kYXRlLnR4dCA8LSBtYXguZGF0ZSAlPiUgZm9ybWF0KCclZCAlYiAlWScpDQpgYGANCg0KYGBge3J9DQojY2xlYW4gZGF0YQ0KY2xlYW5EYXRhIDwtIGZ1bmN0aW9uKGRhdGEpIHsNCiAgIyMgcmVtb3ZlIHNvbWUgY29sdW1ucw0KICBkYXRhICU8PiUgc2VsZWN0KC1jKFByb3ZpbmNlLlN0YXRlLCBMYXQsIExvbmcpKSAlPiUgcmVuYW1lKGNvdW50cnk9Q291bnRyeS5SZWdpb24pDQogICMjIGNvbnZlcnQgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0DQogIGRhdGEgJTw+JSBnYXRoZXIoa2V5PWRhdGUsIHZhbHVlPWNvdW50LCAtY291bnRyeSkNCiAgIyMgY29udmVydCBmcm9tIGNoYXJhY3RlciB0byBkYXRlDQogIGRhdGEgJTw+JSBtdXRhdGUoZGF0ZSA9IGRhdGUgJT4lIG1keSgpKQ0KICAjIyBhZ2dyZWdhdGUgYnkgY291bnRyeQ0KICBkYXRhICU8PiUgZ3JvdXBfYnkoY291bnRyeSwgZGF0ZSkgJT4lIHN1bW1hcmlzZShjb3VudD1zdW0oY291bnQsIG5hLnJtPVQpKSAlPiUgYXMuZGF0YS5mcmFtZSgpDQogIHJldHVybihkYXRhKQ0KfQ0KIyMgY2xlYW4gdGhlIHRocmVlIGRhdGEgc2V0cw0KZGF0YS5jb25maXJtZWQgPC0gZGZfY29uZiAlPiUgY2xlYW5EYXRhKCkgJT4lIHJlbmFtZShjb25maXJtZWQ9Y291bnQpDQpkYXRhLmRlYXRocyA8LSBkZl9kZWF0aHMgJT4lIGNsZWFuRGF0YSgpICU+JSByZW5hbWUoZGVhdGhzPWNvdW50KQ0KZGF0YS5yZWNvdmVyZWQgPC0gZGZfcmVjb3ZlciAlPiUgY2xlYW5EYXRhKCkgJT4lIHJlbmFtZShyZWNvdmVyZWQ9Y291bnQpDQpkYXRhIDwtIGRhdGEuY29uZmlybWVkICU+JSBtZXJnZShkYXRhLmRlYXRocywgYWxsPVQpICU+JSBtZXJnZShkYXRhLnJlY292ZXJlZCwgYWxsPVQpDQpkYXRhDQojIyBjb3VudHJpZXMvcmVnaW9ucyB3aXRoIGNvbmZpcm1lZCBjYXNlcywgZXhjbC4gY3J1aXNlIHNoaXBzDQpjb3VudHJpZXMgPC0gZGF0YSAlPiUgcHVsbChjb3VudHJ5KSAlPiUgc2V0ZGlmZignQ3J1aXNlIFNoaXAnKQ0KZGF0YSANCmBgYA0KDQoNCmBgYHtyfQ0KZGF0YS53b3JsZCA8LSBkYXRhICU+JSBncm91cF9ieShkYXRlKSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50cnk9J1dvcmxkJywNCiAgICAgICAgICAgIGNvbmZpcm1lZCA9IHN1bShjb25maXJtZWQsIG5hLnJtPVQpLA0KICAgICAgICAgICAgZGVhdGhzID0gc3VtKGRlYXRocywgbmEucm09VCksDQogICAgICAgICAgICByZWNvdmVyZWQgPSBzdW0ocmVjb3ZlcmVkLCBuYS5ybT1UKSkNCmRhdGEgJTw+JSByYmluZChkYXRhLndvcmxkKQ0KZGF0YQ0KZGF0YSAlPD4lIG11dGF0ZShjdXJyZW50LmNvbmZpcm1lZCA9IGNvbmZpcm1lZCAtIGRlYXRocyAtIHJlY292ZXJlZCkNCmRhdGENCg0KYGBgDQoNCmBgYHtyfQ0KI3JhdGUNCmRhdGEgJTw+JSBhcnJhbmdlKGNvdW50cnksIGRhdGUpDQpuIDwtIG5yb3coZGF0YSkNCmRheTEgPC0gbWluKGRhdGEkZGF0ZSkNCmRhdGEgJTw+JSBtdXRhdGUobmV3LmNvbmZpcm1lZCA9IGlmZWxzZShkYXRlID09IGRheTEsIE5BLCBjb25maXJtZWQgLSBsYWcoY29uZmlybWVkLCBuPTEpKSwNCiAgICAgICAgICAgICAgICAgbmV3LmRlYXRocyA9IGlmZWxzZShkYXRlID09IGRheTEsIE5BLCBkZWF0aHMgLSBsYWcoZGVhdGhzLCBuPTEpKSwNCiAgICAgICAgICAgICAgICAgbmV3LnJlY292ZXJlZCA9IGlmZWxzZShkYXRlID09IGRheTEsIE5BLCByZWNvdmVyZWQgLSBsYWcocmVjb3ZlcmVkLCBuPTEpKSkNCmRhdGEgJTw+JSBtdXRhdGUobmV3LmNvbmZpcm1lZCA9IGlmZWxzZShuZXcuY29uZmlybWVkIDwgMCwgMCwgbmV3LmNvbmZpcm1lZCksDQogICAgICAgICAgICAgICAgIG5ldy5kZWF0aHMgPSBpZmVsc2UobmV3LmRlYXRocyA8IDAsIDAsIG5ldy5kZWF0aHMpLA0KICAgICAgICAgICAgICAgICBuZXcucmVjb3ZlcmVkID0gaWZlbHNlKG5ldy5yZWNvdmVyZWQgPCAwLCAwLCBuZXcucmVjb3ZlcmVkKSkNCiMjIGRlYXRoIHJhdGUgYmFzZWQgb24gdG90YWwgZGVhdGhzIGFuZCByZWNvdmVyZWQgY2FzZXMNCmRhdGEgJTw+JSBtdXRhdGUocmF0ZS51cHBlciA9ICgxMDAgKiBkZWF0aHMgLyAoZGVhdGhzICsgcmVjb3ZlcmVkKSkgJT4lIHJvdW5kKDEpKQ0KIyMgbG93ZXIgYm91bmQ6IGRlYXRoIHJhdGUgYmFzZWQgb24gdG90YWwgY29uZmlybWVkIGNhc2VzDQpkYXRhICU8PiUgbXV0YXRlKHJhdGUubG93ZXIgPSAoMTAwICogZGVhdGhzIC8gY29uZmlybWVkKSAlPiUgcm91bmQoMSkpDQojIyBkZWF0aCByYXRlIGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgZGVhdGgvcmVjb3ZlcmVkIG9uIGV2ZXJ5IHNpbmdsZSBkYXkNCmRhdGEgJTw+JSBtdXRhdGUocmF0ZS5kYWlseSA9ICgxMDAgKiBuZXcuZGVhdGhzIC8gKG5ldy5kZWF0aHMgKyBuZXcucmVjb3ZlcmVkKSkgJT4lIHJvdW5kKDEpKQ0KVmlldyhkYXRhKQ0KYGBgDQoNCmBgYHtyfQ0KIyMgY29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQNCmRhdGEubG9uZyA8LSBkYXRhICU+JQ0KICBzZWxlY3QoYyhjb3VudHJ5LCBkYXRlLCBjb25maXJtZWQsIGN1cnJlbnQuY29uZmlybWVkLCByZWNvdmVyZWQsIGRlYXRocykpICU+JQ0KICBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtYyhjb3VudHJ5LCBkYXRlKSkNCiMjIHNldCBmYWN0b3IgbGV2ZWxzIHRvIHNob3cgdGhlbSBpbiBhIGRlc2lyYWJsZSBvcmRlcg0KZGF0YS5sb25nICU8PiUgbXV0YXRlKHR5cGU9cmVjb2RlX2ZhY3Rvcih0eXBlLCBjb25maXJtZWQ9J1RvdGFsIENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnQuY29uZmlybWVkPSdDdXJyZW50IENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlY292ZXJlZD0nUmVjb3ZlcmVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVhdGhzPSdEZWF0aHMnKSkNClZpZXcoZGF0YS5sb25nKQ0KYGBgDQoNCmBgYHtyfQ0KIyNOdW1iZXIgb2YgY2FzZSBXb3JsZA0Kd29ybGQgPC0gZmlsdGVyKGRhdGEubG9uZyxjb3VudHJ5ID09ICdXb3JsZCcpDQpwbG90MSA8LSB3b3JsZCAlPiUgZmlsdGVyKHR5cGUgIT0gJ1RvdGFsIENvbmZpcm1lZCcpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1jb3VudCkpICsNCiAgZ2VvbV9hcmVhKGFlcyhmaWxsPXR5cGUpLCBhbHBoYT0wLjUpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ051bWJlcnMgb2YgQ2FzZXMgV29ybGR3aWRlIC0gJywgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygncmVkJywgJ2dyZWVuJywgJ2JsYWNrJykpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9NyksDQogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZT11bml0KDAuMiwgJ2NtJyksDQogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTYpLA0KICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NyksDQogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90MiA8LSB3b3JsZCAlPiUNCiAgZ2dwbG90KGFlcyh4PWRhdGUsIHk9Y291bnQpKSArDQogIGdlb21fbGluZShhZXMoY29sb3I9dHlwZSkpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ051bWJlcnMgb2YgQ2FzZXMgV29ybGR3aWRlIChsb2cgc2NhbGUpIC0gJywgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ3B1cnBsZScsICdyZWQnLCAnZ3JlZW4nLCAnYmxhY2snKSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb249J2JvdHRvbScsDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCksDQogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZT11bml0KDAuMiwgJ2NtJyksDQogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTE0KSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTE0KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZzEwJykNCiMjIHNob3cgdHdvIHBsb3RzIHNpZGUgYnkgc2lkZQ0KZ3JpZC5hcnJhbmdlKHBsb3QxLCBwbG90MiwgbmNvbD0yKQ0KYGBgDQoNCmBgYHtyfQ0KIyMgQ3VycmVudCBDb25maXJtZWQgQ2FzZXMNCmRhdGEud29ybGQgPC0gZGF0YSAlPiUgZmlsdGVyKGNvdW50cnk9PSdXb3JsZCcpDQpuIDwtIG5yb3coZGF0YS53b3JsZCkNCnBsb3QxIDwtIGdncGxvdChkYXRhLndvcmxkLCBhZXMoeD1kYXRlLCB5PWN1cnJlbnQuY29uZmlybWVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nQ3VycmVudCBDb25maXJtZWQgQ2FzZXMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90MiA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1uZXcuY29uZmlybWVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nRGFpbHkgTmV3IENvbmZpcm1lZCBDYXNlcycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCiMjIHNob3cgdHdvIHBsb3RzIHNpZGUgYnkgc2lkZQ0KZ3JpZC5hcnJhbmdlKHBsb3QxLCBwbG90MiwgbmNvbD0yKQ0KVmlldyhkYXRhLndvcmxkKQ0KYGBgDQoNCmBgYHtyfQ0KIyMgYSBzY2F0dGVyIHBsb3Qgd2l0aCBhIHNtb290aGVkIGxpbmUgYW5kIHZlcnRpY2FsIHgtYXhpcyBsYWJlbHMNCnBsb3QxIDwtIGdncGxvdChkYXRhLndvcmxkLCBhZXMoeD1kYXRlLCB5PWRlYXRocykpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J0FjY3VtdWxhdGl2ZSBEZWF0aHMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90MiA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1yZWNvdmVyZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdBY2N1bXVsYXRpdmUgUmVjb3ZlcmVkIENhc2VzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdDMgPC0gZ2dwbG90KGRhdGEud29ybGQsIGFlcyh4PWRhdGUsIHk9bmV3LmRlYXRocykpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J05ldyBEZWF0aHMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90NCA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1uZXcucmVjb3ZlcmVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nTmV3IFJlY292ZXJlZCBDYXNlcycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCiMjIHNob3cgZm91ciBwbG90cyB0b2dldGhlciwgd2l0aCAyIHBsb3RzIGluIGVhY2ggcm93DQpncmlkLmFycmFuZ2UocGxvdDEsIHBsb3QyLCBwbG90MywgcGxvdDQsIG5yb3c9MikNCmBgYA0KDQoNCg0KYGBge3J9DQojIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdCwgZm9yIGRyYXdpbmcgYXJlYSBwbG90cw0KcmF0ZXMubG9uZyA8LSBkYXRhICU+JQ0KICBzZWxlY3QoYyhjb3VudHJ5LCBkYXRlLCByYXRlLnVwcGVyLCByYXRlLmxvd2VyLCByYXRlLmRhaWx5KSkgJT4lDQogIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jKGNvdW50cnksIGRhdGUpKQ0KIyBzZXQgZmFjdG9yIGxldmVscyB0byBzaG93IHRoZW0gaW4gYSBkZXNpcmFibGUgb3JkZXINCnJhdGVzLmxvbmcgJTw+JSBtdXRhdGUodHlwZT1yZWNvZGVfZmFjdG9yKHR5cGUsIHJhdGUuZGFpbHk9J0RhaWx5JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmF0ZS51cHBlcj0nVXBwZXIgYm91bmQnKSkNCmBgYA0KDQpgYGB7cn0NCiMjIHJhbmtpbmcgYnkgY29uZmlybWVkIGNhc2VzDQpkYXRhLmxhdGVzdC5hbGwgPC0gZGF0YSAlPiUgZmlsdGVyKGRhdGUgPT0gbWF4KGRhdGUpKSAlPiUNCiAgc2VsZWN0KGNvdW50cnksIGRhdGUsY29uZmlybWVkLCBuZXcuY29uZmlybWVkLCBjdXJyZW50LmNvbmZpcm1lZCwNCiAgICAgICAgIHJlY292ZXJlZCwgZGVhdGhzLCBuZXcuZGVhdGhzLCBkZWF0aC5yYXRlPXJhdGUubG93ZXIpICU+JQ0KICBtdXRhdGUocmFua2luZyA9IGRlbnNlX3JhbmsoZGVzYyhjb25maXJtZWQpKSkNCiNWaWV3KGRhdGEubGF0ZXN0LmFsbCkNCmsgPC0gMjANCiMjIHRvcCAyMCBjb3VudHJpZXM6IDIxIGluY2wuICdXb3JsZCcNCnRvcC5jb3VudHJpZXMgPC0gZGF0YS5sYXRlc3QuYWxsICU+JSBmaWx0ZXIocmFua2luZyA8PSBrICsgMSkgJT4lDQogIGFycmFuZ2UocmFua2luZykgJT4lIHB1bGwoY291bnRyeSkgJT4lIGFzLmNoYXJhY3RlcigpDQp0b3AuY291bnRyaWVzICU+JSBzZXRkaWZmKCdXb3JsZCcpICU+JSBwcmludCgpDQoNCmBgYA0KDQpgYGB7cn0NCmRhdGEubGF0ZXN0IDwtIGRhdGEubGF0ZXN0LmFsbCAlPiUgZmlsdGVyKCFpcy5uYShjb3VudHJ5KSkgJT4lDQogIG11dGF0ZShjb3VudHJ5PWlmZWxzZShyYW5raW5nIDw9IGsgKyAxLCBhcy5jaGFyYWN0ZXIoY291bnRyeSksICdPdGhlcnMnKSkgJT4lDQogIG11dGF0ZShjb3VudHJ5PWNvdW50cnkgJT4lIGZhY3RvcihsZXZlbHM9Yyh0b3AuY291bnRyaWVzLCAnT3RoZXJzJykpKQ0KZGF0YS5sYXRlc3QgJTw+JSBncm91cF9ieShjb3VudHJ5KSAlPiUNCiAgc3VtbWFyaXNlKGNvbmZpcm1lZD1zdW0oY29uZmlybWVkKSwgbmV3LmNvbmZpcm1lZD1zdW0obmV3LmNvbmZpcm1lZCksDQogICAgICAgICAgICBjdXJyZW50LmNvbmZpcm1lZD1zdW0oY3VycmVudC5jb25maXJtZWQpLA0KICAgICAgICAgICAgcmVjb3ZlcmVkPXN1bShyZWNvdmVyZWQpLCBkZWF0aHM9c3VtKGRlYXRocyksIG5ldy5kZWF0aHM9c3VtKG5ldy5kZWF0aHMpKSAlPiUNCiAgbXV0YXRlKGRlYXRoLnJhdGU9KDEwMCAqIGRlYXRocy9jb25maXJtZWQpICU+JSByb3VuZCgxKSkgDQpkYXRhLmxhdGVzdA0KZGF0YS5sYXRlc3QgJTw+JSBzZWxlY3QoYyhjb3VudHJ5LCBjb25maXJtZWQsIGRlYXRocywgZGVhdGgucmF0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3LmNvbmZpcm1lZCwgbmV3LmRlYXRocywgY3VycmVudC5jb25maXJtZWQscmVjb3ZlcmVkKSkgJT4lDQogIG11dGF0ZShyZWNvdmVyLnJhdGU9KDEwMCAqIHJlY292ZXJlZC9jb25maXJtZWQpICU+JSByb3VuZCgxKSkNCmRhdGEubGF0ZXN0DQpkZl9wb3AgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gcG9wdWxhdGlvbiAiKSkNCmRmX3BvcCA8LSBhcy5kYXRhLmZyYW1lKGRmX3BvcCkNCmRmX3BvcCA8LSByZW5hbWUoZGZfcG9wLCJjb3VudHJ5Ij0iQ291bnRyeSIpDQpkZl9wb3ANCmRhdGEubGF0ZXN0IDwtIG1lcmdlKHggPSBkYXRhLmxhdGVzdCwgeSA9IGRmX3BvcCwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgDQpkYXRhLmxhdGVzdCA8LSByZW5hbWUoZGF0YS5sYXRlc3QsInBvcHVsYXRpb24iID0gIlBvcHVsYXRpb24gKDIwMjApIikNCmRhdGEubGF0ZXN0DQpkYXRhLmxhdGVzdCAgJTw+JSBzZWxlY3QoYyhjb3VudHJ5LCBjb25maXJtZWQsIGRlYXRocywgZGVhdGgucmF0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3LmNvbmZpcm1lZCwgbmV3LmRlYXRocywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudC5jb25maXJtZWQscmVjb3ZlcmVkLHJlY292ZXIucmF0ZSxwb3B1bGF0aW9uKSkgJT4lDQogIG11dGF0ZShjb25maXJtLnJhdGU9KDEwMCAqY29uZmlybWVkL3BvcHVsYXRpb24pICU+JSByb3VuZCgxKSkNCmRhdGEubGF0ZXN0DQoNCmBgYA0KYGBge3J9DQpkYXRhLmxhdGVzdCAlPiUgbXV0YXRlKGRlYXRoLnJhdGU9ZGVhdGgucmF0ZSAlPiUgZm9ybWF0KG5zbWFsbD0xKSAlPiUgcGFzdGUwKCclJykpDQoNCg0KYGBgDQoNCmBgYHtyfQ0KIyMgY29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQsIGZvciBkcmF3aW5nIGFyZWEgcGxvdHMNCmRhdGEubGF0ZXN0LmxvbmcgPC0gZGF0YS5sYXRlc3QgJT4lIGZpbHRlcihjb3VudHJ5IT0nV29ybGQnKSAlPiUNCiAgZ2F0aGVyKGtleT10eXBlLCB2YWx1ZT1jb3VudCwgLWNvdW50cnkpDQojIyBzZXQgZmFjdG9yIGxldmVscyB0byBzaG93IHRoZW0gd2l0aCBwcm9wZXIgdGV4dCBhbmQgaW4gYSBkZXNpcmFibGUgb3JkZXINCmRhdGEubGF0ZXN0LmxvbmcgJTw+JSBtdXRhdGUodHlwZT1yZWNvZGVfZmFjdG9yKHR5cGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25maXJtZWQ9J1RvdGFsIENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWF0aHM9J1RvdGFsIERlYXRocycsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWF0aC5yYXRlPSdEZWF0aCBSYXRlICglKScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcuY29uZmlybWVkPSdOZXcgQ29uZmlybWVkIChjb21wYXJlZCB3aXRoIG9uZSBkYXkgYmVmb3JlKScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcuZGVhdGhzPSdOZXcgRGVhdGhzIChjb21wYXJlZCB3aXRoIG9uZSBkYXkgYmVmb3JlKScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJyZW50LmNvbmZpcm1lZD0nQ3VycmVudCBDb25maXJtZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjb3Zlci5yYXRlID0gJ1JlY292ZXIgUmF0ZSglKScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25maXJtLnJhdGUgPSAnQ29uZmlybWVkIFJhdGUoJSknKSkNCiNWaWV3KGRhdGEubGF0ZXN0LmxvbmcpDQpkYXRhLm9uZS5kZW0gPC0gZmlsdGVyKGRhdGEubGF0ZXN0LmxvbmcsdHlwZT09J1RvdGFsIENvbmZpcm1lZCcNCiAgICAgICAgICAgICAgICAgICAgICAgfCB0eXBlPT0nVG90YWwgRGVhdGhzJw0KICAgICAgICAgICAgICAgICAgICAgICB8IHR5cGU9PSdDdXJyZW50IENvbmZpcm1lZCcpDQpkYXRhLnR3by5kZW0gPC0gZmlsdGVyKGRhdGEubGF0ZXN0LmxvbmcsdHlwZT09J0NvbmZpcm1lZCBSYXRlKCUpJw0KICAgICAgICAgICAgICAgICAgICAgICAjfCB0eXBlPT0nTmV3IENvbmZpcm1lZCAoY29tcGFyZWQgd2l0aCBvbmUgZGF5IGJlZm9yZSknDQogICAgICAgICAgICAgICAgICAgICAgICN8IHR5cGU9PSdOZXcgRGVhdGhzIChjb21wYXJlZCB3aXRoIG9uZSBkYXkgYmVmb3JlKScNCiAgICAgICAgICAgICAgICAgICAgICAgfCB0eXBlPT0nRGVhdGggUmF0ZSAoJSknDQogICAgICAgICAgICAgICAgICAgICAgIHwgdHlwZT09J1JlY292ZXIgUmF0ZSglKScpDQpkYXRhLnR3by5kZW0NCmBgYA0KDQpgYGB7cn0NCiMjIGJhciBjaGFydA0KZGF0YS5vbmUuZGVtICU+JSBnZ3Bsb3QoYWVzKHg9Y291bnRyeSwgeT1jb3VudCwgZmlsbD1jb3VudHJ5LCBncm91cD1jb3VudHJ5KSkgKw0KICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1jb3VudCwgeT1jb3VudCksIHNpemU9Miwgdmp1c3Q9MCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJycpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ1RvcCAyMCBDb3VudHJpZXMgd2l0aCBNb3N0IENvbmZpcm1lZCBDYXNlcyAtICcsIG1heC5kYXRlLnR4dCkpICsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lPSdDb3VudHJ5JywgbGFiZWxzPWFlcyhjb3VudCkpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSdub25lJywNCiAgICAgICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMSksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT03KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBmYWNldF93cmFwKH50eXBlLCBuY29sPTEsIHNjYWxlcz0nZnJlZV95JykNCg0KYGBgDQpgYGB7cn0NCmRhdGEudHdvLmRlbSRmYWNldCA8LSBmYWN0b3IoZGF0YS50d28uZGVtJHR5cGUsIGxldmVscyA9IGMoJ0NvbmZpcm1lZCBSYXRlKCUpJywgJ1JlY292ZXIgUmF0ZSglKScsJ0RlYXRoIFJhdGUgKCUpJykpDQpkYXRhLnR3by5kZW0gJT4lIA0KICBnZ3Bsb3QoYWVzKHg9Y291bnRyeSwgeT1jb3VudCwgZmlsbD1jb3VudHJ5LCBncm91cD1jb3VudHJ5KSkgKw0KICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1jb3VudCwgeT1jb3VudCksIHNpemU9Miwgdmp1c3Q9MCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJycpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ1RvcCAyMCBDb3VudHJpZXMgd2l0aCBNb3N0IENvbmZpcm1lZCBDYXNlcyAtICcsIG1heC5kYXRlLnR4dCkpICsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lPSdDb3VudHJ5JywgbGFiZWxzPWFlcyhjb3VudCkpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSdub25lJywNCiAgICAgICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMSksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT05KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9NixhbmdsZT00NSwgaGp1c3Q9MSkpICsNCiAgZmFjZXRfd3JhcCh+ZmFjZXQsIG5jb2w9MSwgc2NhbGVzPSdmcmVlX3knKQ0KYGBgDQoNCmBgYHtyfQ0KI2RmX2dkcA0KZGZfZ2RwMjAxOSA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBnZHAxOSIpKQ0KZGZfZ2RwMjAxOSA8LSBhcy5kYXRhLmZyYW1lKGRmX2dkcDIwMTkpDQpkZl9nZHAyMDE5DQoNCmBgYA0KYGBge3J9DQojaGVhbHRocmFua2luZw0KZGZfaGVhbHQgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gaGVhbHRocmFua2luZyIpKQ0KZGZfaGVhbHQgPC0gYXMuZGF0YS5mcmFtZShkZl9oZWFsdCkNCmRmX2hlYWx0IDwtIHNlbGVjdChkZl9oZWFsdCxjKCJjb3VudHJ5IiwiaGVhbHRoQ2FyZUluZGV4IikpDQpkZl9oZWFsdA0KYGBgDQoNCmBgYHtyfQ0KI1RvcDIwUG9ybmh1Yg0KZGZfcG9ybmh1YiA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBQb3JuaHViIikpDQpkZl9wb3JuaHViIDwtIGFzLmRhdGEuZnJhbWUoZGZfcG9ybmh1YikNCmRmX3Bvcm5odWINCg0KYGBgDQoNCg0KYGBge3J9DQojdGVtcA0KZGZfdGVtcCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBBdmdfV29ybGRfVGVtcF8yMDIwIikpDQpkZl90ZW1wIDwtIGFzLmRhdGEuZnJhbWUoZGZfdGVtcCkNCmRmX2NpdHkgPC0gc2VsZWN0KGRmX3RlbXAsYygiQ291bnRyeSIsIkNpdHkiKSkgJT4lDQogIHJlbmFtZShjb3VudHJ5PUNvdW50cnkpICU+JSANCiAgcmVuYW1lKGNpdHk9Q2l0eSkNCm51bW9mY2l0eSA8LSBhZ2dyZWdhdGUoY2l0eSB+IGNvdW50cnksIGRhdGEgPSBkZl9jaXR5LCBsZW5ndGgpDQpkZl90ZW1wIDwtIHNlbGVjdChkZl90ZW1wLGMoIkNvdW50cnkiLCJBcHIiLCJNYXkiLCJKdW4iLCJKdWwiLCJBdWciKSkgJT4lDQogIHJlbmFtZShjb3VudHJ5PUNvdW50cnkpDQpkZl90ZW1wIDwtIGRhdGEuZnJhbWUoY291bnRyeT1kZl90ZW1wWywxXSxhdmc9cm93TWVhbnMoZGZfdGVtcFssLTFdKSkNCmRmX3RlbXAgPC0gZGZfdGVtcCAlPD4lIGdyb3VwX2J5KGNvdW50cnkpICU+JSBzdW1tYXJpc2UoYXZnX3RlbXAgPSBtZWFuKGF2ZyxuYS5ybSA9IFRSVUUpKQ0KZGZfdGVtcCA8LSBkZl90ZW1wICU+JSBtdXRhdGUoY291bnRyeT1pZmVsc2UoY291bnRyeT09IlVuaXRlZCBTdGF0ZXMiLCJVUyIsIGNvdW50cnkgKSApIA0KZGZfdGVtcCRhdmdfdGVtcCA8LSBkZl90ZW1wJGF2Z190ZW1wICU+JSANCiAgc3ByaW50ZihkZl90ZW1wJGF2Z190ZW1wLCBmbXQgPSAnJSMuMWYnKSAlPiUNCiAgYXMubnVtZXJpYyhkZl90ZW1wJGF2Z190ZW1wKQ0KZGZfdGVtcA0KYGBgDQoNCg0KDQpgYGB7cn0NCiNUb3AgMjAgd2l0aCBnZHANCmRhdGEubG9uZ0dEUCA8LSBkZl9nZHAgJT4lIGdhdGhlcihrZXk9eWVhciwgdmFsdWU9R0RQLCAtYyhjb3VudHJ5KSkNCmRhdGEudG9wIDwtIGRhdGEubGF0ZXN0ICU+JSBmaWx0ZXIoY291bnRyeSE9J1dvcmxkJykNCmRhdGEudG9wIDwtIGhlYWQoZGF0YS50b3AsMjApDQpWaWV3KGRhdGEubGF0ZXN0KQ0KI21lcmdlDQptZXJnY291bnRyeSA9IGZ1bmN0aW9uKGRhdGExLGRhdGEyKXsNCiAgZGF0YSA8LSBtZXJnZSh4ID0gZGF0YTEsIHkgPSBkYXRhMiwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgDQogIHJldHVybihkYXRhKQ0KfQ0KZGF0YS50b3Aud29ybGQgPC0gbWVyZ2UoeCA9IGRhdGEudG9wLCB5ID0gZGZfZ2RwMjAxOSwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgJT4lIA0KICBzZWxlY3QoLWMoY29kZSxyYW5rLG5ldy5jb25maXJtZWQsbmV3LmRlYXRocyxjdXJyZW50LmNvbmZpcm1lZCxwb3B1bGF0aW9uKSkgJT4lIA0KICByZW5hbWUoR0RQPSJHRFAgKG1pbGxpb25zIG9mIFVTIGRvbGxhcnMpIikNCg0KZGF0YS50b3Aud29ybGQgPC0gbWVyZ2UoeCA9IGRhdGEudG9wLndvcmxkLCB5ID0gZGZfaGVhbHQsIGJ5ID0gImNvdW50cnkiLCBhbGwueCA9IFRSVUUpICU+JQ0KICByZW5hbWUoaGVhbHRoY2FyZT0iaGVhbHRoQ2FyZUluZGV4IikNCiNkYXRhLnRvcC53b3JsZCA8LSBtZXJnY291bnRyeShkYXRhLnRvcC53b3JsZCwgZGZfdGVtcCkNCg0KZGF0YS50b3Aud29ybGQgPC0gbWVyZ2UoeCA9IGRhdGEudG9wLndvcmxkLCB5ID0gZGZfcG9ybmh1YiwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgJT4lDQogIHJlbmFtZShQb3JuaHViID0gIlBvcm5odWJJbmRleCglKSIpDQoNCmRhdGEudG9wLndvcmxkIDwtIG1lcmdjb3VudHJ5KGRhdGEudG9wLndvcmxkLCBkZl90ZW1wKQ0KaW5kZXggPC0gaXMubmEoZGF0YS50b3Aud29ybGQpDQpkYXRhLnRvcC53b3JsZFtpbmRleF0gPC0gMA0KZGF0YS50b3Aud29ybGQNClZpZXcoZGF0YS50b3Aud29ybGQpDQoNCm5vcm1hbGl6ZSA9IGZ1bmN0aW9uKGRhdGEpew0KICAjcmV0dXJuICgoZGF0YSAtIG1pbihkYXRhLG5hLnJtID0gVFJVRSkpLyhtYXgoZGF0YSxuYS5ybSA9IFRSVUUpIC0gbWluKGRhdGEsbmEucm0gPSBUUlVFKSkpDQogIHogPC0gc2NhbGUoZGF0YSk7DQogIHRhbmgoei8yKQ0KfQ0Kbm9ybV9kYXRhID0gYXMuZGF0YS5mcmFtZShhcHBseShkYXRhLnRvcC53b3JsZFssMjoxMl0sMixub3JtYWxpemUpKQ0KY29ycl9kYXRhIDwtIG5vcm1fZGF0YQ0Kbm9ybV9kYXRhJGNvdW50cnkgPC0gYygiQXJnZW50aW5hIiwiQmFuZ2xhZGVzaCIsIkJyYXppbCIsIkNoaWxlIiwiQ29sb21iaWEiLCJGcmFuY2UiLCJHZXJtYW55IiwiSW5kaWEiLCJJcmFuIiwiSXRhbHkiLCJNZXhpY28iLCJQYWtpc3RhbiIsIlBlcnUiLCJSdXNzaWEiLCJzYXVkaSBBcmFiaWEiLCJTb3V0aCBBZnJpY2EiLCJTcGFpbiIsIlR1cmtleSIsIlVuaXRlZCBLaW5nZG9tIiwiVVMiKQ0KI1ZpZXcobm9ybV9kYXRhKQ0KDQoNCm5vcm1fZGF0YV9wbG90IDwtIHNlbGVjdChub3JtX2RhdGEsImNvdW50cnkiLCJjb25maXJtLnJhdGUiLCJkZWF0aC5yYXRlIiwicmVjb3Zlci5yYXRlIiwiaGVhbHRoY2FyZSIsIlBvcm5odWIiLCJHRFAiLCJhdmdfdGVtcCIpDQpub3JtX2RhdGFfcGxvdCAlPD4lIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jKGNvdW50cnkpKQ0KbGV2ZWxfb3JkZXIgPC0gZmFjdG9yKG5vcm1fZGF0YV9wbG90JHR5cGUsIA0KICAgICAgICAgICAgICAgICAgICAgIGxldmVsID0gYygiR0RQIiwiYXZnX3RlbXAiLCJoZWFsdGhjYXJlIiwicmVjb3Zlci5yYXRlIiwiZGVhdGgucmF0ZSIsImNvbmZpcm0ucmF0ZSIsIlBvcm5odWIiKSkNCmdncGxvdChkYXRhID0gbm9ybV9kYXRhX3Bsb3QsIGFlcyh4PWNvdW50cnksIHk9bGV2ZWxfb3JkZXIsIGZpbGw9Y291bnQpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gInBpbmsiLCBoaWdoID0gImJsdWUiKSArDQogIHhsYWIoIiIpICsNCiAgeWxhYigiIikgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCx2anVzdCA9IDEpKSsNCiAgdGhlbWUoDQogICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLA0KICAgICNsZWdlbmQucG9zaXRpb24gPSAibm9uZSINCiAgKQ0KDQogIA0KYGBgDQoNCg0KDQoNCg0KDQpgYGB7cn0NCiNjb3JyZWxhdGlvbg0KY29ycl9kYXRhICU8PiUgc2VsZWN0KGMoR0RQLGNvbmZpcm0ucmF0ZSxkZWF0aC5yYXRlLHJlY292ZXIucmF0ZSxoZWFsdGhjYXJlLGF2Z190ZW1wLFBvcm5odWIpKQ0KaGVhZChjb3JyX2RhdGEpDQpjb3IoY29ycl9kYXRhKQ0KZ2djb3JycGxvdChjb3IoY29ycl9kYXRhKSxoYy5vcmRlciA9IFRSVUUsDQogICAgICAgICAgIG91dGxpbmUuY29sb3IgPSAid2hpdGUiLA0KICAgICAgICAgICBjb2xvcnMgPSBjKCIjNkQ5RUMxIiwid2hpdGUiLCIjRTQ2NzI2IiksDQogICAgICAgICAgIGxhYiA9IFRSVUUpDQpgYGANCmBgYHtyfQ0KZGYgPC0gZGF0YS5sb25nICU+JSBmaWx0ZXIoY291bnRyeSAlaW4lIHRvcC5jb3VudHJpZXMpICU8PiUNCiAgbXV0YXRlKGNvdW50cnk9Y291bnRyeSAlPiUgZmFjdG9yKGxldmVscz1jKHRvcC5jb3VudHJpZXMpKSkNCmRmICU+JSBmaWx0ZXIoY291bnRyeSAhPSAnV29ybGQnICYgdHlwZSAhPSAnVG90YWwgQ29uZmlybWVkJykgJT4lDQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWNvdW50LCBmaWxsPXR5cGUpKSArDQogIGdlb21fYXJlYShhbHBoYT0wLjUpICsNCiMgeGxhYignJykgKyB5bGFiKCcnKSArDQogIGxhYnModGl0bGU9cGFzdGUwKCdOdW1iZXJzIG9mIENPVklELTE5IENhc2VzIGluIFRvcCAyMCBDb3VudHJpZXMgLSAnLA0KICAgICAgICAgICAgICAgICAgICBtYXguZGF0ZS50eHQpKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCdyZWQnLCAnZ3JlZW4nLCAnYmxhY2snKSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb249J2JvdHRvbScsDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksDQogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZT11bml0KDAuNCwgJ2NtJyksDQogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwNCiAgICAgICAgc3RyaXAudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTEyKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBmYWNldF93cmFwKH5jb3VudHJ5LCBuY29sPTQsIHNjYWxlcz0nZnJlZV95JykNCg0KYGBgDQpgYGB7cn0NCnAgPC0gZGYgJT4lIGZpbHRlcihjb3VudHJ5ICE9ICdXb3JsZCcpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1jb3VudCwgY29sb3I9dHlwZSkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBsYWJzKHRpdGxlPXBhc3RlMCgnTnVtYmVycyBvZiBDT1ZJRC0xOSBDYXNlcyBpbiBUb3AgMjAgQ291bnRyaWVzIChsb2cgc2NhbGUpIC0gJywNCiAgICAgICAgICAgICAgICAgICAgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ3B1cnBsZScsICdyZWQnLCAnZ3JlZW4nLCAnYmxhY2snKSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb249J2JvdHRvbScsDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksDQogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZT11bml0KDAuNCwgJ2NtJyksDQogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEwKSwNCiAgICAgICAgc3RyaXAudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTEwKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEwKSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZzEwJykNCnAgKyBmYWNldF93cmFwKH5jb3VudHJ5LCBuY29sPTQsIHNjYWxlcz0nZnJlZV95JykNCmBgYA0KYGBge3J9DQpkYXRhLndvcmxkICU8PiUgYXJyYW5nZShkZXNjKGRhdGUpKSAlPiUNCiAgc2VsZWN0KGMoZGF0ZSwgY29uZmlybWVkLCBkZWF0aHMsIHJlY292ZXJlZCwgY3VycmVudC5jb25maXJtZWQsbmV3LmNvbmZpcm1lZCwgbmV3LmRlYXRocywgbmV3LnJlY292ZXJlZCwgcmF0ZS5sb3dlciwgcmF0ZS51cHBlciwgcmF0ZS5kYWlseSkpDQpkYXRhLndvcmxkICU+JQ0KICBtdXRhdGUocmF0ZS51cHBlciA9IHJhdGUudXBwZXIgJT4lIGZvcm1hdChuc21hbGw9MSkgJT4lIHBhc3RlMCgnXFwlJyksDQogICAgICAgICByYXRlLmxvd2VyID0gcmF0ZS5sb3dlciAlPiUgZm9ybWF0KG5zbWFsbD0xKSAlPiUgcGFzdGUwKCdcXCUnKSwNCiAgICAgICAgIHJhdGUuZGFpbHkgPSByYXRlLmRhaWx5ICU+JSBmb3JtYXQobnNtYWxsPTEpICU+JSBwYXN0ZTAoJ1xcJScpKSANCmBgYA0KYGBge3J9DQojc2Fyc18yMDAzDQpkZl9zYXJzIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIHNhcnNfMjAwM191cGRhdGUiKSkNCmRmX3NhcnMgPC0gYXMuZGF0YS5mcmFtZShkZl9zYXJzKQ0KZGZfc2Fycw0KYGBgDQoNCg0KYGBge3J9DQojIyBjb252ZXJ0IGZyb20gY2hhcmFjdGVyIHRvIGRhdGUNCiNkYXRlc1NhciA8LSBhcy5EYXRlKGRmX3NhcnMkRGF0ZSxmb3JtYXQgPSAiJW0vJWQvJXkiKQ0KDQpkZl9zYXJzICU8PiUgIG11dGF0ZShEYXRlID0gYXMuRGF0ZShkZl9zYXJzJERhdGUsZm9ybWF0ID0gIiVtLyVkLyV5IikpDQpkZl9zYXJzDQpgYGANCg0KYGBge3J9DQojIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdA0KZGF0YVNhci5sb25nIDwtIGRmX3NhcnMgJT4lDQogIHNlbGVjdChjKERhdGUsIGNvdW50cnksIEN1bXVsYXRpdmVfbnVtYmVyICwgTnVtYmVyX2RlYXRocywgTnVtYmVyX3JlY292ZXJlZCkpICU+JQ0KICBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtYyhjb3VudHJ5LCBEYXRlKSkNCiMjIHNldCBmYWN0b3IgbGV2ZWxzIHRvIHNob3cgdGhlbSBpbiBhIGRlc2lyYWJsZSBvcmRlcg0KZGF0YVNhci5sb25nICU8PiUgbXV0YXRlKHR5cGU9cmVjb2RlX2ZhY3Rvcih0eXBlLCBDdW11bGF0aXZlX251bWJlciA9J0N1bXVsYXRpdmUgTnVtYmVyJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVtYmVyX2RlYXRocyA9J051bWJlciBvZiBkZWF0aHMnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOdW1iZXJfcmVjb3ZlcmVkID0nTnVtYmVyIG9mIHJlY292ZXJlZCcpKQ0KVmlldyhkYXRhU2FyLmxvbmcpDQogDQpgYGANCg0KDQoNCg0KDQoNCmBgYHtyfQ0KI0NvdmlkX1RoYWlsYW5kDQpkZl90aGFpIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGNvdmlkMTlfVGhhaWxhbmQiKSkNCmRmX3RoYWkgPC0gYXMuZGF0YS5mcmFtZShkZl90aGFpKQ0KVmlldyhkZl90aGFpKQ0KYGBgDQoNCmBgYHtyfQ0KI2NsZWFuIENvdmlkX1RoYWlsYW5kDQpkYXRlcy50aCA8LSBkZl90aGFpWywyXSU+JSBtZHkoKQ0KcmFuZ2UoZGF0ZXMudGgpDQptaW4uZGF0ZS50aCA8LSBtaW4oZGF0ZXMudGgpDQptYXguZGF0ZS50aCA8LSBtYXgoZGF0ZXMudGgpDQptaW4uZGF0ZS50eHQudGggPC0gbWluLmRhdGUudGggJT4lIGZvcm1hdCgnJWQgJWIgJVknKQ0KbWF4LmRhdGUudHh0LnRoIDwtIG1heC5kYXRlLnRoICU+JSBmb3JtYXQoJyVkICViICVZJykNCmBgYA0KYGBge3J9DQpkZl90aGFpJGFubm91bmNlX2RhdGUgPC0gbWR5KGRmX3RoYWkkYW5ub3VuY2VfZGF0ZSkNCmRmX3RoYWkkbm90aWZpY2F0aW9uX2RhdGUgPC0gbWR5KGRmX3RoYWkkbm90aWZpY2F0aW9uX2RhdGUpDQpkZl90aGFpIA0KYGBgDQpgYGB7cn0NCmRmX3RoYWkgPC0gZGZfdGhhaSAlPiUgc2VsZWN0KCFOby4pICU+JSBzZWxlY3QoIW5vdGlmaWNhdGlvbl9kYXRlKSAlPiUgDQogIGdyb3VwX2J5KGFubm91bmNlX2RhdGUpDQpkZl90aGFpDQpgYGANCmBgYHtyfQ0KIyBUb3RhbCBjb25maXJtZWQgY2FzZXMgaW4gVGhhaWxhbmQNCmRhdGEudGhhaS5jb3VudCA8LSBkZl90aGFpICU+JQ0KICBzZWxlY3QoYW5ub3VuY2VfZGF0ZSkgJT4lDQogIHN1bW1hcmlzZShjb21maXJtZWQgPSBuKCkpICAlPiUgYXMuZGF0YS5mcmFtZSgpDQpkYXRhLnRoYWkuY291bnQkY3VtdWxhdGl2ZV9jb25maXJtZWQgPC0gY3Vtc3VtKGRhdGEudGhhaS5jb3VudFssIDJdKQ0KZGF0YS50aGFpLmNvdW50DQpgYGANCg0KDQpgYGB7cn0NCiMjIFRoYWkgQ29uZmlybWVkIENhc2VzIChKYW4gMjAyMCAtIEphbiAyMDIxDQpwbG90MSA8LSBnZ3Bsb3QoZGF0YS50aGFpLmNvdW50LCBhZXMoeD1hbm5vdW5jZV9kYXRlLCB5PWN1bXVsYXRpdmVfY29uZmlybWVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYigiICIpICsgeWxhYigiQ291bnQiKSArIGxhYnModGl0bGU9J1RoYWkgQ3VtdWxhdGl2ZSBDb25maXJtZWQgQ2FzZXMgKEphbiAyMDIwIC0gSmFuIDIwMjEpJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdDIgPC0gZ2dwbG90KGRhdGEudGhhaS5jb3VudCwgYWVzKHg9YW5ub3VuY2VfZGF0ZSwgeT1jb21maXJtZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICAgIHhsYWIoIiAiKSArIHlsYWIoIkNvdW50IikrIGxhYnModGl0bGU9J1RoYWkgQ29uZmlybWVkIENhc2VzIChKYW4gMjAyMCAtIEphbiAyMDIxIGxvZyBzY2FsZSknKSArDQogICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKStzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZzEwJykNCiMjIHNob3cgdHdvIHBsb3RzIHNpZGUgYnkgc2lkZQ0KZ3JpZC5hcnJhbmdlKHBsb3QxLCBwbG90MiwgbmNvbD0xKQ0KYGBgDQpgYGB7cn0NCiMjIFRoYWkgQ29uZmlybWVkIENhc2VzIChKYW4gMjAyMCAtIEphbiAyMDIxKSBsb2cgc2NhbGUNCnBsb3QxIDwtIGdncGxvdChkYXRhLnRoYWkuY291bnQsIGFlcyh4PWFubm91bmNlX2RhdGUsIHk9Y3VtdWxhdGl2ZV9jb25maXJtZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCIgIikgKyB5bGFiKCJDb3VudCIpICsgbGFicyh0aXRsZT0nVGhhaSBDdW11bGF0aXZlIENvbmZpcm1lZCBDYXNlcyAoSmFuIDIwMjAgLSBKYW4gMjAyMSBsb2cgc2NhbGUpJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKStzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZzEwJykNCnBsb3QyIDwtIGdncGxvdChkYXRhLnRoYWkuY291bnQsIGFlcyh4PWFubm91bmNlX2RhdGUsIHk9Y29tZmlybWVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYigiICIpICsgeWxhYigiQ291bnQiKSsgbGFicyh0aXRsZT0nVGhhaSBDb25maXJtZWQgQ2FzZXMgKEphbiAyMDIwIC0gSmFuIDIwMjEgbG9nIHNjYWxlKScpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkrc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSdsb2cxMCcpDQojIyBzaG93IHR3byBwbG90cyBzaWRlIGJ5IHNpZGUNCmdyaWQuYXJyYW5nZShwbG90MSwgcGxvdDIsIG5jb2w9MSkNCmBgYA0KDQpgYGB7cn0NCiMgQ29uZmlybWVkIGNhc2VzIGRpdmlkZWQgYnkgc2V4IChnZW5kZXIpDQpkZl90aGFpJHNleFtkZl90aGFpJHNleCA9PSAiIl0gPC0gIlVua25vd24iDQpkYXRhLnRoYWkuZ2VuZGVyIDwtIGRmX3RoYWkgJT4lDQogIGdyb3VwX2J5KHNleCkgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogIG11dGF0ZShwZXJjZW50ID0gKGNvdW50IC8gc3VtKGNvdW50KSAqIDEwMCkgJT4lIHJvdW5kKDIpKSAlPiUNCiAgZmlsdGVyKHBlcmNlbnQ+MSklPiUNCiAgI211dGF0ZShwb3MgPSBjdW1zdW0ocGVyY2VudCkgLSAwLjUqcGVyY2VudCkgJT4lDQogIGFycmFuZ2UoZGVzYyhwZXJjZW50KSkNCg0KZGF0YS50aGFpLmdlbmRlcg0KYGBgDQoNCmBgYHtyfQ0KZGF0YS50aGFpLmdlbmRlciRzZXggPC0gZmFjdG9yKGRhdGEudGhhaS5nZW5kZXIkc2V4LCBsZXZlbHMgPSBhcy5jaGFyYWN0ZXIoZGF0YS50aGFpLmdlbmRlciRzZXgpKQ0KZGF0YS50aGFpLmdlbmRlciRzZXgNCmcudGguZ2VuZGVyIDwtIGRhdGEudGhhaS5nZW5kZXIgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSAiIiwgeSA9IHBlcmNlbnQsIGZpbGwgPSBzZXgpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDEpICsNCiAgY29vcmRfcG9sYXIoInkiKSArDQogIHRoZW1lX3ZvaWQoKSArDQogIGxhYnModGl0bGU9J0dlbmRlciBvZiBUaGFpIENvbmZpcm1lZCBDYXNlcyAoSmFuIDIwMjAgLSBKYW4gMjAyMSknKSsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChwZXJjZW50LCAiJSIpKSwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gNSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKSANCmcudGguZ2VuZGVyDQpgYGANCg0KYGBge3J9DQojIENvbmZpcm1lZCBjYXNlcyBkaXZpZGVkIGJ5IHByb3ZpbmNlX29mX29uc2V0DQpkYXRhLnRoYWkub25zZXQgPC0gZGZfdGhhaSAlPiUNCiAgICBncm91cF9ieShwcm92aW5jZV9vZl9vbnNldCkgJT4lDQogICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgICBhcnJhbmdlKGRlc2MoY291bnQpKSU+JQ0KICAgIHJlbmFtZShvbnNldD0iY291bnQiKSU+JQ0KICAgIHJlbmFtZShwcm92aW5jZT0icHJvdmluY2Vfb2Zfb25zZXQiKQ0KZGF0YS50aGFpLm9uc2V0JHByb3ZpbmNlW2RhdGEudGhhaS5vbnNldCRwcm92aW5jZSA9PSAiIl0gPC0gIlVua25vd24iDQpkYXRhLnRoYWkub25zZXQNCmBgYA0KYGBge3J9DQojIENvbmZpcm1lZCBjYXNlcyBkaXZpZGVkIGJ5IHByb3ZpbmNlX29mX2lzb2xhdGlvbiANCmRhdGEudGhhaS5pc29sYXRpb24gPC0gZGZfdGhhaSAlPiUNCiAgICBncm91cF9ieShwcm92aW5jZV9vZl9pc29sYXRpb24pICU+JQ0KICAgIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogICAgYXJyYW5nZShkZXNjKGNvdW50KSklPiUNCiAgICByZW5hbWUoaXNvbGF0aW9uID0iY291bnQiKSU+JQ0KICAgIHJlbmFtZShwcm92aW5jZT0icHJvdmluY2Vfb2ZfaXNvbGF0aW9uIikNCg0KZGF0YS50aGFpLmlzb2xhdGlvbiRwcm92aW5jZVtkYXRhLnRoYWkuaXNvbGF0aW9uJHByb3ZpbmNlID09ICIiXSA8LSAiVW5rbm93biINCmRhdGEudGhhaS5pc29sYXRpb24NCmBgYA0KYGBge3J9DQpkYXRhLnByb3ZpbmNlIDwtIG1lcmdlKHggPSBkYXRhLnRoYWkub25zZXQsIHkgPSBkYXRhLnRoYWkuaXNvbGF0aW9uLCBieSA9ICJwcm92aW5jZSIsIGFsbC54ID0gVFJVRSkgDQpkYXRhLnByb3ZpbmNlIDwtIGRhdGEucHJvdmluY2UlPiVmaWx0ZXIob25zZXQ+MTI1KQ0KZGF0YS5wcm92aW5jZQ0KYGBgDQpgYGB7cn0NCmRhdGEucHJvdmluY2UubG9uZyA8LSBkYXRhLnByb3ZpbmNlICU+JQ0KICAgIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jKHByb3ZpbmNlKSkNCmRhdGEucHJvdmluY2UubG9uZw0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEucHJvdmluY2UubG9uZyxhZXMoeD1wcm92aW5jZSx5PWNvdW50KSkrDQogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIscG9zaXRpb24gPSAiZG9kZ2UiLGFlcyhmaWxsPXR5cGUpKSsNCiAgICBsYWJzKHRpdGxlID0gIlByb3ZpbmNlIG9mIG9uc2V0IGFuZCBQcm92aW5jZSBvZiBpc29sYXRpb24gaW4gVGhhaWxhbmQiKSsNCiAgICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgI2xlZ2VuZC5wb3NpdGlvbj0nbm9uZScsDQogICAgICAgICAgICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyKSwNCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMCksDQogICAgICAgICAgICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSsgeGxhYignJykgKyB5bGFiKCdDb3VudCcpDQpgYGANCg0KDQoNCg0KDQoNCmBgYHtyfQ0KIyBDb25maXJtZWQgY2FzZXMgZGl2aWRlZCBieSByaXNrDQpkYXRhLnRoYWkucmlzayA8LSBkZl90aGFpICU+JQ0KICBncm91cF9ieShyaXNrKSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGNvdW50KSkNCmRhdGEudGhhaS5yaXNrDQpgYGANCg0KYGBge3J9DQojIENvbmZpcm1lZCBjYXNlcyBkaXZpZGVkIGJ5IHJpc2sNCmRhdGEudGhhaS5yaXNrIDwtIGRmX3RoYWkgJT4lDQogIGdyb3VwX2J5KHJpc2spICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQ0KICBtdXRhdGUocGVyY2VudCA9IChjb3VudCAvIHN1bShjb3VudCkgKiAxMDApICU+JSByb3VuZCgyKSkgJT4lDQogIGZpbHRlcihwZXJjZW50PjAuNzYpICU+JQ0KICBhcnJhbmdlKGRlc2MocGVyY2VudCkpIA0KZGF0YS50aGFpLnJpc2sNCmBgYA0KYGBge3J9DQpkYXRhLnRoYWkucmlzayRyaXNrW2RhdGEudGhhaS5yaXNrJHJpc2sgPT0gIkMiXSA8LSAiQ2xvc2UgY29udGFjdCB3aXRoIHRoZSBwYXRpZW50Ig0KZGF0YS50aGFpLnJpc2skcmlza1tkYXRhLnRoYWkucmlzayRyaXNrID09ICJDR3VzdERyIFNhbXV0IFNhR0dBRiJdIDwtICJDbHVzdGVyIFNhbXV0IFNha2hvbiINCmRhdGEudGhhaS5yaXNrJHJpc2tbZGF0YS50aGFpLnJpc2skcmlzayA9PSAiRiJdIDwtICJTdGF0ZSBRdWFyYW50aW5lIg0KZGF0YS50aGFpLnJpc2skcmlza1tkYXRhLnRoYWkucmlzayRyaXNrID09ICJHIl0gPC0gIkdvIHRvIGEgY3Jvd2RlZCBwbGFjZSINCmRhdGEudGhhaS5yaXNrJHJpc2tbZGF0YS50aGFpLnJpc2skcmlzayA9PSAiQSJdIDwtICJQZW9wbGUgdHJhdmVsbGluZyBmcm9tIGFicm9hZCINCmRhdGEudGhhaS5yaXNrJHJpc2tbZGF0YS50aGFpLnJpc2skcmlzayA9PSAiVUZHRkF3RiJdIDwtICJVbmtub3duIg0KZGF0YS50aGFpLnJpc2skcmlza1tkYXRhLnRoYWkucmlzayRyaXNrID09ICJEIl0gPC0gIkNhcmVlciBhdCByaXNrIg0KZGF0YS50aGFpLnJpc2skcmlza1tkYXRhLnRoYWkucmlzayRyaXNrID09ICJDR3VzdERyIFJheUFGZyJdIDwtICJDbHVzdGVyIFJheW9uZyINCmRhdGEudGhhaS5yaXNrJHJpc2tbZGF0YS50aGFpLnJpc2skcmlzayA9PSAiQ0d1c3REciBQYXR0YXlhIENhc0NGQSJdIDwtICJDbHVzdGVyIFBhdHRheWEgQ2FzaW5vIg0KZGF0YS50aGFpLnJpc2skcmlza1tkYXRhLnRoYWkucmlzayRyaXNrID09ICJDR3VzdERyIEFGZyBUR0FGZyJdIDwtICJDbHVzdGVyIEFuZyBUaG9uZyINCmRhdGEudGhhaS5yaXNrDQpgYGANCg0KDQoNCg0KYGBge3J9DQpkYXRhLnRoYWkucmlzayRyaXNrIDwtIGZhY3RvcihkYXRhLnRoYWkucmlzayRyaXNrLCBsZXZlbHMgPSBhcy5jaGFyYWN0ZXIoZGF0YS50aGFpLnJpc2skcmlzaykpDQpkYXRhLnRoYWkucmlzayRyaXNrDQpnLnRoLnJpc2sgPC0gZGF0YS50aGFpLnJpc2sgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSAiIiwgeSA9IHBlcmNlbnQsIGZpbGwgPSByaXNrKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAwLjUpICsNCiAgY29vcmRfcG9sYXIoInkiKSArDQogIHRoZW1lX3ZvaWQoKSArDQogIGxhYnModGl0bGU9J1Jpc2sgb2YgVGhhaSBDb25maXJtZWQgQ2FzZXMoSmFuIDIwMjAgLSBKYW4gMjAyMSknKSsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChwZXJjZW50LCAiJSIpKSwgY29sb3IgPSAiQmxhY2siLCBzaXplID0gMywgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKSANCmcudGgucmlzaw0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgQ29uZmlybWVkIGNhc2VzIGRpdmlkZWQgYnkgYWdlDQpkYXRhLnRoYWkuYWdlIDwtIGRmX3RoYWkgJT4lDQogIGdyb3VwX2J5KGFnZSxzZXgpICU+JSANCiAgZmlsdGVyKGFnZSAhPSAwKSU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQ0KICBhcnJhbmdlKGRlc2MoY291bnQpKQ0KZGF0YS50aGFpLmFnZQ0KYGBgDQpgYGB7cn0NCmdncGxvdChkYXRhLnRoYWkuYWdlLGFlcyh4PWFnZSx5PWNvdW50LGZpbGw9c2V4KSkrZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpKw0KICAgICAgbGFicyh0aXRsZT0nQWdlIG9mIFRoYWkgQ29uZmlybWVkIENhc2VzIChTdGFjayknKStndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFQpKQ0KDQpgYGANCg0KYGBge3J9DQojIENvbmZpcm1lZCBjYXNlcyBkaXZpZGVkIGJ5IG5hdGlvbmFsaXR5DQoNCg0KZGF0YS50aGFpLm5hdGlvbmFsaXR5IDwtIGRmX3RoYWkgJT4lDQogIGdyb3VwX2J5KG5hdGlvbmFsaXR5KSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgZmlsdGVyKGNvdW50ID4gMTEpJT4lDQogIGFycmFuZ2UoZGVzYyhjb3VudCkpDQpkYXRhLnRoYWkubmF0aW9uYWxpdHkkbmF0aW9uYWxpdHlbZGF0YS50aGFpLm5hdGlvbmFsaXR5JG5hdGlvbmFsaXR5ID09ICI/Pz8/Pz8/PyJdIDwtICJVbmtub3duIg0KZGF0YS50aGFpLm5hdGlvbmFsaXR5JG5hdGlvbmFsaXR5W2RhdGEudGhhaS5uYXRpb25hbGl0eSRuYXRpb25hbGl0eSA9PSAiIl0gPC0gIlVua25vd24iDQpkYXRhLnRoYWkubmF0aW9uYWxpdHkkbmF0aW9uYWxpdHlbZGF0YS50aGFpLm5hdGlvbmFsaXR5JG5hdGlvbmFsaXR5ID09ICJCdXJtYSJdIDwtICJNeWFubWFyIg0KZGF0YS50aGFpLm5hdGlvbmFsaXR5DQpgYGANCmBgYHtyfQ0KZ2dwbG90KGRhdGEudGhhaS5uYXRpb25hbGl0eSkrIA0KICBnZW9tX2JhcihhZXMoeD1uYXRpb25hbGl0eSx5PWNvdW50LGZpbGwgPSBjb3VudCksc3RhdCA9ICJpZGVudGl0eSIsc2hvdy5sZWdlbmQgPSBGKSsNCiAgICBnZ3RpdGxlKCdOYXRpb25hbGl0eSBvZiBUaGFpIENvbmZpcm1lZCBDYXNlcyAobG9nIHNjYWxlKScpKw0KICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSsNCiAgICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkrDQogICAgeGxhYigiIikreWxhYigiTnVtYmVyIG9mIE5hdGlvbmFsaXR5IikrDQogICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZzEwJykrY29vcmRfZmxpcCgpK3NjYWxlX2ZpbGxfZ3JhZGllbnQobG93PSJibHVlIixoaWdoID0gInJlZCIpDQpgYGANCg0KDQoNCmBgYHtyfQ0KI0NvdmlkX1VTDQpkZl91cyA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBjb3ZpZFVzIikpDQpkZl91cyA8LSBhcy5kYXRhLmZyYW1lKGRmX3VzKQ0KZGZfdXMNCmBgYA0KDQpgYGB7cn0NCiNjbGVhbiBDb3ZpZF9VUw0KZGF0ZXMudXMgPC0gZGZfdXNbLDJdJT4lIG1keSgpDQpyYW5nZShkYXRlcy51cykNCm1pbi5kYXRlLnVzIDwtIG1pbihkYXRlcy51cykNCm1heC5kYXRlLnVzIDwtIG1heChkYXRlcy51cykNCm1pbi5kYXRlLnR4dC51cyA8LSBtaW4uZGF0ZS51cyAlPiUgZm9ybWF0KCclZCAlYiAlWScpDQptYXguZGF0ZS50eHQudXMgPC0gbWF4LmRhdGUudXMgJT4lIGZvcm1hdCgnJWQgJWIgJVknKQ0KYGBgDQoNCmBgYHtyfQ0KZGZfdXMkZGF0ZSA8LSBtZHkoZGZfdXMkZGF0ZSkNCmRmX3VzDQpgYGANCg0KDQpgYGB7cn0NCmRmX3VzIDwtIGRmX3VzICU+JSBzZWxlY3QoIU15VW5rbm93bkNvbHVtbikgJT4lIHNlbGVjdCghZmlwcykgJT4lIA0KICBncm91cF9ieShkYXRlKQ0KZGZfdXMNCmBgYA0KDQoNCmBgYHtyfQ0KZGF0YS51cy50b3RhbCA8LSBkZl91cyAlPiUgZ3JvdXBfYnkoZGF0ZSkgJT4lDQogIHN1bW1hcmlzZShzdGF0ZSA9J1VTJywNCiAgICAgICAgICAgIGNhc2VzID0gc3VtKGRlYXRocyxuYS5ybT1UKSwNCiAgICAgICAgICAgIGRlYXRocyA9IHN1bShkZWF0aHMsbmEucm09VCkpDQpWaWV3KGRhdGEudXMudG90YWwpDQpgYGANCg0KDQpgYGB7cn0NCmRhdGEudXMubGF0ZXN0IDwtIGRmX3VzICU+JWZpbHRlcihkYXRlPT1tYXguZGF0ZS51cykNClZpZXcoZGF0YS51cy5sYXRlc3QpDQpgYGANCg0KYGBge3J9DQpkYXRhLnVzLmxhdGVzdCAlPD4lIG11dGF0ZShyYW5raW5nID0gZGVuc2VfcmFuayhkZXNjKGNhc2VzKSkpICU+JWFycmFuZ2UocmFua2luZykgDQp0b3AudXMgPC0gZGF0YS51cy5sYXRlc3RbLDJdDQp0b3AudXMNCmBgYA0KYGBge3J9DQojIExpc3Qgb2YgdG9wIDIwIHN0YXRlDQprIDwtIDE5DQpkYXRhLnVzLnRvcCA8LSBkYXRhLnVzLmxhdGVzdCAlPiUNCiAgICBmaWx0ZXIocmFua2luZyA8PSBrKzEpICU+JSANCiAgICBhcnJhbmdlKHJhbmtpbmcpIA0KVmlldyhkYXRhLnVzLnRvcCkNCnVzLnN0YXRlLnRvcCA8LSBkYXRhLnVzLnRvcCAlPiUgcHVsbChzdGF0ZSkgJT4lIGFzLmNoYXJhY3RlcigpDQp1cy5zdGF0ZS50b3AgICU+JSBzZXRkaWZmKCdVUycpICU+JSBwcmludCgpDQpWaWV3KHVzLnN0YXRlLnRvcCkNCmBgYA0KYGBge3J9DQojIGNvbmZpcm1lZCByYXRlICYgZGVhdGggcmF0ZSBvZiB0b3AgMjAgc3RhdGUNCnRvcF91cyA8LSBkYXRhLnVzLmxhdGVzdCAlPiUgZmlsdGVyKHN0YXRlICVpbiUgdXMuc3RhdGUudG9wICYgc3RhdGUgIT0gIlVTIikNClZpZXcodG9wX3VzKQ0KYGBgDQoNCg0KDQoNCg0KDQpgYGB7cn0NCiNnZW5kZXIgaW4gdXMNCmRmX2dlbmRlcl91cyA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBkYXRhX2dlbmRlciIpKQ0KZGZfZ2VuZGVyX3VzIDwtIGFzLmRhdGEuZnJhbWUoZGZfZ2VuZGVyX3VzKQ0KZGZfZ2VuZGVyX3VzIDwtIHNlbGVjdChkZl9nZW5kZXJfdXMsYygiU3RhdGUiLCJNYWxlIiwiRmVtYWxlIikpDQpkZl9nZW5kZXJfdXMgPC0gcmVuYW1lKGRmX2dlbmRlcl91cywic3RhdGUiPSJTdGF0ZSIpDQpkZl9nZW5kZXJfdXMNCmBgYA0KYGBge3J9DQojcG9wdWxhdGlvbiBpbiB1cw0KZGZfcG9wX3VzIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGRhdGFfcG9wdWxhdGlvbiIpKQ0KZGZfcG9wX3VzIDwtIGFzLmRhdGEuZnJhbWUoZGZfcG9wX3VzKQ0KZGZfcG9wX3VzIDwtIHNlbGVjdChkZl9wb3BfdXMsYygiU3RhdGUiLCJQb3B1bGF0aW9uIikpDQpkZl9wb3BfdXMgPC0gcmVuYW1lKGRmX3BvcF91cywic3RhdGUiPSJTdGF0ZSIpDQpkZl9wb3BfdXMNCmBgYA0KYGBge3J9DQojbG9ja2Rvd24gaW4gdXMNCmRmX2xvY2tkb3duX3VzIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGRhdGFfbG9ja2Rvd24iKSkNCmRmX2xvY2tkb3duX3VzIDwtIGFzLmRhdGEuZnJhbWUoZGZfbG9ja2Rvd25fdXMpDQpkZl9sb2NrZG93bl91cyA8LSBzZWxlY3QoZGZfbG9ja2Rvd25fdXMsYygiU3RhdGUiLCJEYXkgbG9ja2Rvd24iKSkNCmRmX2xvY2tkb3duX3VzIDwtIHJlbmFtZShkZl9sb2NrZG93bl91cywic3RhdGUiPSJTdGF0ZSIpDQpkZl9sb2NrZG93bl91cw0KYGBgDQoNCmBgYHtyfQ0KI0dEUCBpbiB1cw0KZGZfZ2RwX3VzIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGdkcF91cyIpKQ0KZGZfZ2RwX3VzIDwtIGFzLmRhdGEuZnJhbWUoZGZfZ2RwX3VzKQ0KZGZfZ2RwX3VzIDwtIHNlbGVjdChkZl9nZHBfdXMsYygiU3RhdGUiLCJHRFBzIikpDQpkZl9nZHBfdXMgPC0gcmVuYW1lKGRmX2dkcF91cywic3RhdGUiPSJTdGF0ZSIpDQpkZl9nZHBfdXMNCmBgYA0KDQpgYGB7cn0NCiNob21lbGVzcyBpbiB1cw0KZGZfaG9tZWxlc3NfdXMgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gdXNfaG9tZWxlc3MiKSkNCmRmX2hvbWVsZXNzX3VzIDwtIGFzLmRhdGEuZnJhbWUoZGZfaG9tZWxlc3NfdXMpDQpkZl9ob21lbGVzc191cyA8LSBzZWxlY3QoZGZfaG9tZWxlc3NfdXMsYygiU3RhdGUiLCJIb21lbGVzcyIpKQ0KZGZfaG9tZWxlc3NfdXMgPC0gcmVuYW1lKGRmX2hvbWVsZXNzX3VzLCJzdGF0ZSI9IlN0YXRlIikNCmRmX2hvbWVsZXNzX3VzDQpgYGANCg0KDQpgYGB7cn0NCiNWaWV3KGRmX3VzKQ0KI21lcmdlDQptZXJnY291bnRyeSA9IGZ1bmN0aW9uKGRhdGExLGRhdGEyKXsNCiAgZGF0YSA8LSBtZXJnZSh4ID0gZGF0YTEsIHkgPSBkYXRhMiwgYnkgPSAic3RhdGUiLCBhbGwueCA9IFRSVUUpIA0KICByZXR1cm4oZGF0YSkNCn0NCg0KZGZfQWxsdXMgPC0gbWVyZ2NvdW50cnkodG9wX3VzLCBkZl9nZW5kZXJfdXMpDQpkZl9BbGx1cyA8LSBtZXJnY291bnRyeShkZl9BbGx1cywgZGZfcG9wX3VzKQ0KZGZfQWxsdXMgPC0gbWVyZ2NvdW50cnkoZGZfQWxsdXMsIGRmX2xvY2tkb3duX3VzKQ0KZGZfQWxsdXMgPC0gbWVyZ2NvdW50cnkoZGZfQWxsdXMsIGRmX2dkcF91cykNCmRmX0FsbHVzIDwtIG1lcmdjb3VudHJ5KGRmX0FsbHVzLCBkZl9ob21lbGVzc191cykNCiNkZl9BbGx1cyA8LSBtZXJnY291bnRyeSh0b3BfdXMsIGRmX0FsbHVzKQ0KVmlldyhkZl9BbGx1cykNCg0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KaW5kZXggPC0gaXMubmEoZGZfQWxsdXMpDQpkZl9BbGx1c1tpbmRleF0gPC0gMA0KDQpub3JtYWxpemUgPSBmdW5jdGlvbihkYXRhKXsNCiAgI3JldHVybiAoKGRhdGEgLSBtaW4oZGF0YSxuYS5ybSA9IFRSVUUpKS8obWF4KGRhdGEsbmEucm0gPSBUUlVFKSAtIG1pbihkYXRhLG5hLnJtID0gVFJVRSkpKQ0KICB6IDwtIHNjYWxlKGRhdGEpOw0KICB0YW5oKHovMikNCn0NCkFsbHVzID0gYXMuZGF0YS5mcmFtZShhcHBseShkZl9BbGx1c1ssMzoxMV0sMixub3JtYWxpemUpKQ0KY29ycl9kYXRhVVMgPC0gQWxsdXMgDQoNClZpZXcoQWxsdXMpDQoNCmBgYA0KDQpgYGB7cn0NCkFsbHVzJHN0YXRlIDwtIGMoZGZfQWxsdXMkc3RhdGUpDQpBbGx1cyA8LSByZW5hbWUoQWxsdXMsImNvbmZpcm1lZCI9ImNhc2VzIikNClZpZXcoQWxsdXMpDQpgYGANCg0KDQpgYGB7cn0NCg0KDQpBbGx1c19wbG90IDwtIHNlbGVjdChBbGx1cywic3RhdGUiLCJjb25maXJtZWQiLCJkZWF0aHMiLCJNYWxlIiwiRmVtYWxlIiwiUG9wdWxhdGlvbiIsIkRheSBsb2NrZG93biIsIkdEUHMiLCJIb21lbGVzcyIpDQpBbGx1c19wbG90ICU8PiUgZ2F0aGVyKGtleT10eXBlLCB2YWx1ZT1jb3VudCwgLWMoc3RhdGUpKQ0KbGV2ZWxfb3JkZXIgPC0gZmFjdG9yKEFsbHVzX3Bsb3QkdHlwZSwgDQogICAgICAgICAgICAgICAgICAgICAgbGV2ZWwgPSBjKCJjb25maXJtZWQiLCJkZWF0aHMiLCJNYWxlIiwiRmVtYWxlIiwiUG9wdWxhdGlvbiIsIkRheSBsb2NrZG93biIsIkdEUHMiLCJIb21lbGVzcyIpKQ0KZ2dwbG90KGRhdGEgPSBBbGx1c19wbG90LCBhZXMoeD1zdGF0ZSwgeT1sZXZlbF9vcmRlciwgZmlsbD1jb3VudCkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAicGluayIsIGhpZ2ggPSAiYmx1ZSIpICsNCiAgeGxhYigiIikgKw0KICB5bGFiKCIiKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLHZqdXN0ID0gMSkpKw0KICB0aGVtZSgNCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgI2xlZ2VuZC5wb3NpdGlvbiA9ICJub25lIg0KICApICtsYWJzKHRpdGxlPSdUaGUgaGVhdG1hcCBvZiBDT1ZJRC0xOSBpbmZlY3Rpb25zIGluIFVTJykNCg0KDQoNCmBgYA0KDQpgYGB7cn0NCmNvcnJfZGF0YVVTIDwtIHJlbmFtZShjb3JyX2RhdGFVUywiRGF5bG9ja2Rvd24iPSJEYXkgbG9ja2Rvd24iKQ0KYGBgDQoNCmBgYHtyfQ0KI2NvcnJlbGF0aW9uDQpjb3JyX2RhdGFVUyAlPD4lIHNlbGVjdChjKGNhc2VzLGRlYXRocyxNYWxlLEZlbWFsZSxQb3B1bGF0aW9uLERheWxvY2tkb3duLEdEUHMsSG9tZWxlc3MpKQ0KaGVhZChjb3JyX2RhdGFVUykNCmNvcihjb3JyX2RhdGFVUykNCmdnY29ycnBsb3QoY29yKGNvcnJfZGF0YVVTKSxoYy5vcmRlciA9IFRSVUUsDQogICAgICAgICAgIG91dGxpbmUuY29sb3IgPSAid2hpdGUiLA0KICAgICAgICAgICBjb2xvcnMgPSBjKCIjNkQ5RUMxIiwid2hpdGUiLCIjRTQ2NzI2IiksDQogICAgICAgICAgIGxhYiA9IFRSVUUpDQpgYGANCg0KDQoNCmBgYHtyfQ0KI1ZpZXcoZGZfdXMpDQojbWVyZ2UNCm1lcmdjb3VudHJ5ID0gZnVuY3Rpb24oZGF0YTEsZGF0YTIpew0KICBkYXRhIDwtIG1lcmdlKHggPSBkYXRhMSwgeSA9IGRhdGEyLCBieSA9ICJzdGF0ZSIsIGFsbC54ID0gVFJVRSkgDQogIHJldHVybihkYXRhKQ0KfQ0KDQpkZi5BbGx1cyA8LSBtZXJnY291bnRyeShkZl91cywgZGZfZ2VuZGVyX3VzKQ0KZGYuQWxsdXMgPC0gbWVyZ2NvdW50cnkoZGYuQWxsdXMsIGRmX3BvcF91cykNCmRmLkFsbHVzIDwtIG1lcmdjb3VudHJ5KGRmLkFsbHVzLCBkZl9sb2NrZG93bl91cykNCmRmLkFsbHVzIDwtIG1lcmdjb3VudHJ5KGRmLkFsbHVzLCBkZl9nZHBfdXMpDQpkZi5BbGx1cyA8LSBtZXJnY291bnRyeShkZi5BbGx1cywgZGZfaG9tZWxlc3NfdXMpDQojZGZfQWxsdXMgPC0gbWVyZ2NvdW50cnkodG9wX3VzLCBkZl9BbGx1cykNClZpZXcoZGZfQWxsdXMpDQoNCmBgYA0KDQpgYGB7cn0NCmluZGV4IDwtIGlzLm5hKGRmLkFsbHVzKQ0KZGYuQWxsdXNbaW5kZXhdIDwtIDANCg0Kbm9ybWFsaXplID0gZnVuY3Rpb24oZGF0YSl7DQogICNyZXR1cm4gKChkYXRhIC0gbWluKGRhdGEsbmEucm0gPSBUUlVFKSkvKG1heChkYXRhLG5hLnJtID0gVFJVRSkgLSBtaW4oZGF0YSxuYS5ybSA9IFRSVUUpKSkNCiAgeiA8LSBzY2FsZShkYXRhKTsNCiAgdGFuaCh6LzIpDQp9DQpBbGx1cyA9IGFzLmRhdGEuZnJhbWUoYXBwbHkoZGYuQWxsdXNbLDM6MTBdLDIsbm9ybWFsaXplKSkNCmNvcnJfZGF0YVVTIDwtIEFsbHVzIA0KDQpWaWV3KEFsbHVzKQ0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KY29ycl9kYXRhVVMgPC0gcmVuYW1lKGNvcnJfZGF0YVVTLCJEYXlsb2NrZG93biI9IkRheSBsb2NrZG93biIpDQpgYGANCg0KYGBge3J9DQojY29ycmVsYXRpb24NCmNvcnJfZGF0YVVTICU8PiUgc2VsZWN0KGMoY2FzZXMsZGVhdGhzLE1hbGUsRmVtYWxlLFBvcHVsYXRpb24sRGF5bG9ja2Rvd24sR0RQcyxIb21lbGVzcykpDQpoZWFkKGNvcnJfZGF0YVVTKQ0KY29yKGNvcnJfZGF0YVVTKQ0KZ2djb3JycGxvdChjb3IoY29ycl9kYXRhVVMpLGhjLm9yZGVyID0gVFJVRSwNCiAgICAgICAgICAgb3V0bGluZS5jb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgIGNvbG9ycyA9IGMoIiM2RDlFQzEiLCJ3aGl0ZSIsIiNFNDY3MjYiKSwNCiAgICAgICAgICAgbGFiID0gVFJVRSkNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K